aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquickscrollview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates/qquickscrollview.cpp')
-rw-r--r--src/quicktemplates/qquickscrollview.cpp192
1 files changed, 173 insertions, 19 deletions
diff --git a/src/quicktemplates/qquickscrollview.cpp b/src/quicktemplates/qquickscrollview.cpp
index 8a352b7f61..5f7c7bf2fd 100644
--- a/src/quicktemplates/qquickscrollview.cpp
+++ b/src/quicktemplates/qquickscrollview.cpp
@@ -98,11 +98,17 @@ public:
QQmlListProperty<QObject> contentData() override;
QQmlListProperty<QQuickItem> contentChildren() override;
QList<QQuickItem *> contentChildItems() const override;
+ QQuickItem* getFirstChild() const override;
QQuickItem *getContentItem() override;
- QQuickFlickable *ensureFlickable(bool content);
- bool setFlickable(QQuickFlickable *flickable, bool content);
+ enum class ContentItemFlag {
+ DoNotSet,
+ Set
+ };
+
+ QQuickFlickable *ensureFlickable(ContentItemFlag contentItemFlag);
+ bool setFlickable(QQuickFlickable *flickable, ContentItemFlag contentItemFlag);
void flickableContentWidthChanged();
void flickableContentHeightChanged();
@@ -127,10 +133,17 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
+ void updateScrollBarWidth();
+ void updateScrollBarHeight();
+
+ void disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar);
bool wasTouched = false;
QQuickFlickable *flickable = nullptr;
bool flickableHasExplicitContentWidth = true;
bool flickableHasExplicitContentHeight = true;
+ bool isUpdatingScrollBar = false;
+ qreal effectiveScrollBarWidth = 0;
+ qreal effectiveScrollBarHeight = 0;
};
QList<QQuickItem *> QQuickScrollViewPrivate::contentChildItems() const
@@ -145,15 +158,23 @@ QQuickItem *QQuickScrollViewPrivate::getContentItem()
{
if (!contentItem)
executeContentItem();
- return ensureFlickable(false);
+ // This function is called by QQuickControl::contentItem() to lazily create
+ // a contentItem, so we don't need to try to set it again.
+ return ensureFlickable(ContentItemFlag::DoNotSet);
}
-QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
+QQuickItem* QQuickScrollViewPrivate::getFirstChild() const
+{
+ return contentChildItems().value(0);
+}
+
+QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (!flickable) {
flickableHasExplicitContentWidth = false;
flickableHasExplicitContentHeight = false;
+ // Pass ourselves as the Flickable's parent item.
auto flickable = new QQuickFlickable(q);
// We almost always want to clip the flickable so that flickable
// contents doesn't show up outside the scrollview. The only time
@@ -163,12 +184,64 @@ QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(bool content)
// child inside the scrollview, and control clipping on it explicit.
flickable->setClip(true);
flickable->setPixelAligned(true);
- setFlickable(flickable, content);
+ setFlickable(flickable, contentItemFlag);
}
return flickable;
}
-bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
+void QQuickScrollViewPrivate::updateScrollBarWidth()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarWidth = effectiveScrollBarWidth;
+ if (auto *vBar = verticalScrollBar()) {
+ if (vBar->policy() == QQuickScrollBar::AlwaysOff || !vBar->isVisible())
+ effectiveScrollBarWidth = 0;
+ else
+ effectiveScrollBarWidth = vBar->width();
+ }
+ if (effectiveScrollBarWidth != oldEffectiveScrollBarWidth) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarWidthChanged();
+ }
+ }
+}
+
+void QQuickScrollViewPrivate::updateScrollBarHeight()
+{
+ Q_Q(QQuickScrollView);
+ qreal oldEffectiveScrollBarHeight = effectiveScrollBarHeight;
+ if (auto *hBar = horizontalScrollBar()) {
+ if (hBar->policy() == QQuickScrollBar::AlwaysOff || !hBar->isVisible())
+ effectiveScrollBarHeight = 0;
+ else
+ effectiveScrollBarHeight = hBar->height();
+ }
+ if (effectiveScrollBarHeight != oldEffectiveScrollBarHeight) {
+ if (!isUpdatingScrollBar) {
+ QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
+ emit q->effectiveScrollBarHeightChanged();
+ }
+
+ }
+}
+
+void QQuickScrollViewPrivate::disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar)
+{
+ if (!scrollBar)
+ return;
+
+ if (scrollBar->vertical) {
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::disconnect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::disconnect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+}
+
+bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, ContentItemFlag contentItemFlag)
{
Q_Q(QQuickScrollView);
if (item == flickable)
@@ -179,8 +252,11 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
if (flickable) {
flickable->removeEventFilter(q);
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(nullptr);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(nullptr);
+ disconnectScrollBarSignals(scrollBar);
+ }
QObjectPrivate::disconnect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -188,7 +264,7 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
}
flickable = item;
- if (content)
+ if (contentItemFlag == ContentItemFlag::Set)
q->setContentItem(flickable);
if (flickable) {
@@ -202,8 +278,18 @@ bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content)
else
flickableContentHeightChanged();
- if (attached)
- QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(flickable);
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ scrollBar->setFlickable(flickable);
+ if (scrollBar->vertical) {
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ QObjectPrivate::connect(scrollBar->vertical, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarWidth);
+ }
+ if (scrollBar->horizontal) {
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::policyChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ QObjectPrivate::connect(scrollBar->horizontal, &QQuickScrollBar::visibleChanged, this, &QQuickScrollViewPrivate::updateScrollBarHeight);
+ }
+ }
QObjectPrivate::connect(flickable->contentItem(), &QQuickItem::childrenChanged, this, &QQuickPanePrivate::contentChildrenChange);
QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::flickableContentWidthChanged);
@@ -305,11 +391,14 @@ void QQuickScrollViewPrivate::setScrollBarsInteractive(bool interactive)
void QQuickScrollViewPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
- if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), true))
+ // If we don't yet have a flickable assigned, and this object is a Flickable,
+ // make it our contentItem.
+ if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), ContentItemFlag::Set))
return;
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the object that was declared as a child of us as a child object of the Flickable.
QQmlListProperty<QObject> data = flickable->flickableData();
data.append(&data, obj);
}
@@ -348,10 +437,11 @@ void QQuickScrollViewPrivate::contentChildren_append(QQmlListProperty<QQuickItem
{
QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
if (!p->flickable)
- p->setFlickable(qobject_cast<QQuickFlickable *>(item), true);
+ p->setFlickable(qobject_cast<QQuickFlickable *>(item), ContentItemFlag::Set);
- QQuickFlickable *flickable = p->ensureFlickable(true);
+ QQuickFlickable *flickable = p->ensureFlickable(ContentItemFlag::Set);
Q_ASSERT(flickable);
+ // Add the item that was declared as a child of us as a child item of the Flickable's contentItem.
QQmlListProperty<QQuickItem> children = flickable->flickableChildren();
children.append(&children, item);
}
@@ -406,6 +496,48 @@ QQuickScrollView::QQuickScrollView(QQuickItem *parent)
setWheelEnabled(true);
}
+QQuickScrollView::~QQuickScrollView()
+{
+ Q_D(QQuickScrollView);
+ QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(this, false));
+ if (attached) {
+ auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
+ d->disconnectScrollBarSignals(scrollBar);
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarWidth
+ \since 6.6
+
+ This property holds the effective width of the vertical scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarWidth()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarWidth;
+}
+
+/*!
+ \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarHeight
+ \since 6.6
+
+ This property holds the effective height of the horizontal scrollbar.
+ When the scrollbar policy is \c QQuickScrollBar::AlwaysOff or the scrollbar
+ is not visible, this property is \c 0.
+
+ \sa {ScrollBar::policy}
+*/
+qreal QQuickScrollView::effectiveScrollBarHeight()
+{
+ Q_D(QQuickScrollView);
+ return d->effectiveScrollBarHeight;
+}
+
/*!
\qmlproperty list<QtObject> QtQuick.Controls::ScrollView::contentData
\qmldefault
@@ -543,7 +675,7 @@ void QQuickScrollView::componentComplete()
Q_D(QQuickScrollView);
QQuickPane::componentComplete();
if (!d->contentItem)
- d->ensureFlickable(true);
+ d->ensureFlickable(QQuickScrollViewPrivate::ContentItemFlag::Set);
}
void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
@@ -557,7 +689,25 @@ void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldIte
auto newItemAsFlickable = qobject_cast<QQuickFlickable *>(newItem);
if (newItem && !newItemAsFlickable)
qmlWarning(this) << "ScrollView only supports Flickable types as its contentItem";
- d->setFlickable(newItemAsFlickable, false);
+ // This is called by QQuickControlPrivate::setContentItem_helper, so no need to
+ // try to set it as the contentItem.
+ d->setFlickable(newItemAsFlickable, QQuickScrollViewPrivate::ContentItemFlag::DoNotSet);
+ // We do, however, need to set us as its parent item, as setContentItem_helper will only
+ // do so if the item doesn't already have a parent. If newItem wasn't declared as our
+ // child and was instead imperatively assigned, it may already have a parent item,
+ // which we'll need to override.
+ if (newItem) {
+ newItem->setParentItem(this);
+
+ // Make sure that the scroll bars are stacked in front of the flickable,
+ // otherwise events won't get through to them.
+ QQuickScrollBar *verticalBar = d->verticalScrollBar();
+ if (verticalBar)
+ verticalBar->stackAfter(newItem);
+ QQuickScrollBar *horizontalBar = d->horizontalScrollBar();
+ if (horizontalBar)
+ horizontalBar->stackAfter(newItem);
+ }
}
QQuickPane::contentItemChange(newItem, oldItem);
}
@@ -573,10 +723,14 @@ void QQuickScrollView::contentSizeChange(const QSizeF &newSize, const QSizeF &ol
// exception is if the application has assigned a content size
// directly to the scrollview, which will then win even if the
// application has assigned something else to the flickable.
- if (d->hasContentWidth || !d->flickableHasExplicitContentWidth)
+ if (d->hasContentWidth || !d->flickableHasExplicitContentWidth) {
d->flickable->setContentWidth(newSize.width());
- if (d->hasContentHeight || !d->flickableHasExplicitContentHeight)
+ d->updateScrollBarWidth();
+ }
+ if (d->hasContentHeight || !d->flickableHasExplicitContentHeight) {
d->flickable->setContentHeight(newSize.height());
+ d->updateScrollBarHeight();
+ }
}
}