aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/imports/templates/qtquicktemplates2plugin.cpp1
-rw-r--r--src/quicktemplates2/qquickscrollbar.cpp128
-rw-r--r--src/quicktemplates2/qquickscrollbar_p.h12
-rw-r--r--src/quicktemplates2/qquickscrollbar_p_p.h14
-rw-r--r--tests/auto/controls/data/tst_scrollbar.qml48
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)
+ }
}