diff options
-rw-r--r-- | src/imports/templates/qtquicktemplates2plugin.cpp | 1 | ||||
-rw-r--r-- | src/quicktemplates2/qquickscrollbar.cpp | 128 | ||||
-rw-r--r-- | src/quicktemplates2/qquickscrollbar_p.h | 12 | ||||
-rw-r--r-- | src/quicktemplates2/qquickscrollbar_p_p.h | 14 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_scrollbar.qml | 48 |
5 files changed, 187 insertions, 16 deletions
diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp index 37bbbe66..ffe2d50a 100644 --- a/src/imports/templates/qtquicktemplates2plugin.cpp +++ b/src/imports/templates/qtquicktemplates2plugin.cpp @@ -323,6 +323,7 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri) qmlRegisterType<QQuickAbstractButton, 4>(uri, 2, 4, "AbstractButton"); qmlRegisterType<QQuickButtonGroup, 4>(uri, 2, 4, "ButtonGroup"); qmlRegisterType<QQuickCheckBox, 4>(uri, 2, 4, "CheckBox"); + qmlRegisterType<QQuickScrollBar, 4>(uri, 2, 4, "ScrollBar"); qmlRegisterType<QQuickSpinBox, 4>(uri, 2, 4, "SpinBox"); } diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp index e75fed5a..aa0094aa 100644 --- a/src/quicktemplates2/qquickscrollbar.cpp +++ b/src/quicktemplates2/qquickscrollbar.cpp @@ -160,6 +160,7 @@ QQuickScrollBarPrivate::QQuickScrollBarPrivate() position(0), stepSize(0), offset(0), + minimumSize(0), active(false), pressed(false), moving(false), @@ -171,6 +172,26 @@ QQuickScrollBarPrivate::QQuickScrollBarPrivate() { } +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); @@ -184,9 +205,9 @@ qreal QQuickScrollBarPrivate::positionAt(const QPointF &point) const { Q_Q(const QQuickScrollBar); if (orientation == Qt::Horizontal) - return (point.x() - q->leftPadding()) / q->availableWidth(); + return logicalPosition(point.x() - q->leftPadding()) / q->availableWidth(); else - return (point.y() - q->topPadding()) / q->availableHeight(); + return logicalPosition(point.y() - q->topPadding()) / q->availableHeight(); } void QQuickScrollBarPrivate::setInteractive(bool enabled) @@ -230,15 +251,14 @@ void QQuickScrollBarPrivate::resizeContent() // - 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 qreal clampedSize = qBound<qreal>(0, size + qMin<qreal>(0, position), 1.0 - position); - const qreal clampedPos = qBound<qreal>(0, position, 1.0 - clampedSize); + const VisualArea visual = visualArea(); if (orientation == Qt::Horizontal) { - contentItem->setPosition(QPointF(q->leftPadding() + clampedPos * q->availableWidth(), q->topPadding())); - contentItem->setSize(QSizeF(q->availableWidth() * clampedSize, q->availableHeight())); + 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() + clampedPos * q->availableHeight())); - contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * clampedSize)); + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + visual.position * q->availableHeight())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * visual.size)); } } @@ -247,8 +267,9 @@ void QQuickScrollBarPrivate::handlePress(const QPointF &point) Q_Q(QQuickScrollBar); QQuickControlPrivate::handlePress(point); offset = positionAt(point) - position; - if (offset < 0 || offset > size) - offset = size / 2; + qreal sz = qMax(size, logicalPosition(minimumSize)); + if (offset < 0 || offset > sz) + offset = sz / 2; q->setPressed(true); } @@ -282,6 +303,15 @@ void QQuickScrollBarPrivate::handleUngrab() 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) { @@ -306,6 +336,8 @@ QQuickScrollBarAttached *QQuickScrollBar::qmlAttachedProperties(QObject *object) 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 { @@ -319,10 +351,12 @@ void QQuickScrollBar::setSize(qreal size) if (qFuzzyCompare(d->size, size)) return; + auto oldVisualArea = d->visualArea(); d->size = size; if (isComponentComplete()) d->resizeContent(); emit sizeChanged(); + d->visualAreaChange(d->visualArea(), oldVisualArea); } /*! @@ -334,6 +368,8 @@ void QQuickScrollBar::setSize(qreal size) 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 { @@ -347,10 +383,12 @@ void QQuickScrollBar::setPosition(qreal position) if (qFuzzyCompare(d->position, position)) return; + auto oldVisualArea = d->visualArea(); d->position = position; if (isComponentComplete()) d->resizeContent(); emit positionChanged(); + d->visualAreaChange(d->visualArea(), oldVisualArea); } /*! @@ -591,6 +629,64 @@ bool QQuickScrollBar::isVertical() const } /*! + \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. @@ -776,9 +872,9 @@ void QQuickScrollBarAttachedPrivate::scrollHorizontal() { QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable); - const qreal viewwidth = f->width(); - const qreal maxxextent = -f->maxXExtent() + f->minXExtent(); - qreal cx = horizontal->position() * (maxxextent + viewwidth) - f->minXExtent(); + const qreal extent = f->contentWidth() - f->minXExtent(); + const qreal cx = horizontal->position() * extent; + if (!qIsNaN(cx) && !qFuzzyCompare(cx, flickable->contentX())) flickable->setContentX(cx); } @@ -787,9 +883,9 @@ void QQuickScrollBarAttachedPrivate::scrollVertical() { QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable); - const qreal viewheight = f->height(); - const qreal maxyextent = -f->maxYExtent() + f->minYExtent(); - qreal cy = vertical->position() * (maxyextent + viewheight) - f->minYExtent(); + const qreal extent = f->contentHeight() - f->minYExtent(); + const qreal cy = vertical->position() * extent; + if (!qIsNaN(cy) && !qFuzzyCompare(cy, flickable->contentY())) flickable->setContentY(cy); } diff --git a/src/quicktemplates2/qquickscrollbar_p.h b/src/quicktemplates2/qquickscrollbar_p.h index 9b417a2b..1ac1d306 100644 --- a/src/quicktemplates2/qquickscrollbar_p.h +++ b/src/quicktemplates2/qquickscrollbar_p.h @@ -69,6 +69,9 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBar : public QQuickControl Q_PROPERTY(Policy policy READ policy WRITE setPolicy NOTIFY policyChanged FINAL REVISION 2) Q_PROPERTY(bool horizontal READ isHorizontal NOTIFY orientationChanged FINAL REVISION 3) Q_PROPERTY(bool vertical READ isVertical NOTIFY orientationChanged FINAL REVISION 3) + Q_PROPERTY(qreal minimumSize READ minimumSize WRITE setMinimumSize NOTIFY minimumSizeChanged FINAL REVISION 4) + Q_PROPERTY(qreal visualSize READ visualSize NOTIFY visualSizeChanged FINAL REVISION 4) + Q_PROPERTY(qreal visualPosition READ visualPosition NOTIFY visualPositionChanged FINAL REVISION 4) public: explicit QQuickScrollBar(QQuickItem *parent = nullptr); @@ -117,6 +120,12 @@ public: bool isHorizontal() const; bool isVertical() const; + qreal minimumSize() const; + void setMinimumSize(qreal minimumSize); + + qreal visualSize() const; + qreal visualPosition() const; + public Q_SLOTS: void increase(); void decrease(); @@ -133,6 +142,9 @@ Q_SIGNALS: Q_REVISION(2) void snapModeChanged(); Q_REVISION(2) void interactiveChanged(); Q_REVISION(2) void policyChanged(); + Q_REVISION(4) void minimumSizeChanged(); + Q_REVISION(4) void visualSizeChanged(); + Q_REVISION(4) void visualPositionChanged(); protected: void mousePressEvent(QMouseEvent *event) override; diff --git a/src/quicktemplates2/qquickscrollbar_p_p.h b/src/quicktemplates2/qquickscrollbar_p_p.h index 431bb383..384348b4 100644 --- a/src/quicktemplates2/qquickscrollbar_p_p.h +++ b/src/quicktemplates2/qquickscrollbar_p_p.h @@ -68,6 +68,17 @@ public: return bar->d_func(); } + struct VisualArea + { + VisualArea(qreal pos, qreal sz) + : position(pos), size(sz) { } + qreal position; + qreal size; + }; + VisualArea visualArea() const; + + qreal logicalPosition(qreal position) const; + qreal snapPosition(qreal position) const; qreal positionAt(const QPointF &point) const; void setInteractive(bool interactive); @@ -79,10 +90,13 @@ public: void handleRelease(const QPointF &point) override; void handleUngrab() override; + void visualAreaChange(const VisualArea &newVisualArea, const VisualArea &oldVisualArea); + qreal size; qreal position; qreal stepSize; qreal offset; + qreal minimumSize; bool active; bool pressed; bool moving; diff --git a/tests/auto/controls/data/tst_scrollbar.qml b/tests/auto/controls/data/tst_scrollbar.qml index bc424a27..a8d33cea 100644 --- a/tests/auto/controls/data/tst_scrollbar.qml +++ b/tests/auto/controls/data/tst_scrollbar.qml @@ -790,4 +790,52 @@ TestCase { if (control.background) tryCompare(control.background, "opacity", 0) } + + function test_minimumSize() { + var container = createTemporaryObject(flickable, testCase) + verify(container) + waitForRendering(container) + + var vertical = scrollBar.createObject(container, {minimumSize: 0.1}) + container.ScrollBar.vertical = vertical + + compare(container.visibleArea.heightRatio, 0.5) + compare(vertical.size, 0.5) + compare(vertical.visualSize, 0.5) + compare(vertical.contentItem.height, 0.5 * vertical.availableHeight) + + container.contentHeight = 2000 + + compare(container.visibleArea.heightRatio, 0.05) + compare(vertical.size, 0.05) + compare(vertical.visualSize, 0.1) + compare(vertical.contentItem.height, 0.1 * vertical.availableHeight) + + verify(container.atYBeginning) + compare(container.visibleArea.yPosition, 0.0) + compare(vertical.position, 0.0) + compare(vertical.visualPosition, 0.0) + compare(vertical.contentItem.y, vertical.topPadding) + + container.contentY = 1900 + + verify(container.atYEnd) + compare(container.visibleArea.yPosition, 0.95) + compare(vertical.position, 0.95) + compare(vertical.visualPosition, 0.9) + compare(vertical.contentItem.y, vertical.topPadding + 0.9 * vertical.availableHeight) + + container.contentHeight = 125 + + compare(container.visibleArea.heightRatio, 0.8) + compare(vertical.size, 0.8) + compare(vertical.visualSize, 0.8) + compare(vertical.contentItem.height, 0.8 * vertical.availableHeight) + + verify(container.atYEnd) + compare(container.visibleArea.yPosition, 0.2) + compare(vertical.position, 0.2) + compare(vertical.visualPosition, 0.2) + compare(vertical.contentItem.y, vertical.topPadding + 0.2 * vertical.availableHeight) + } } |