diff options
Diffstat (limited to 'src/quicktemplates/qquickscrollview.cpp')
-rw-r--r-- | src/quicktemplates/qquickscrollview.cpp | 192 |
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(); + } } } |