1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickscrollview_p.h"
5#include "qquickpane_p_p.h"
6#include "qquickscrollbar_p_p.h"
7
8#include <QtQuick/private/qquickflickable_p.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \qmltype ScrollView
14 \inherits Pane
15//! \nativetype QQuickScrollView
16 \inqmlmodule QtQuick.Controls
17 \since 5.9
18 \ingroup qtquickcontrols-containers
19 \ingroup qtquickcontrols-focusscopes
20 \brief Scrollable view.
21
22 ScrollView provides scrolling for user-defined content. It can be used to
23 either replace a \l Flickable, or to decorate an existing one.
24
25 \image qtquickcontrols-scrollview.png
26
27 The first example demonstrates the simplest usage of ScrollView.
28
29 \snippet qtquickcontrols-scrollview.qml file
30
31 The second example illustrates using an existing \l Flickable, that is,
32 a \l ListView.
33
34 \snippet qtquickcontrols-scrollview-listview.qml file
35
36 \note As of Qt-6.0, ScrollView automatically clips its contents if you
37 don't use a Flickable as a child. If this is not wanted, you can
38 set your own Flickable as a child, and control the \l {Item::}{clip}
39 property on the Flickable explicitly.
40
41 \section2 Sizing
42
43 As with Flickable, there are several things to keep in mind when using
44 ScrollView:
45 \list
46 \li If only a single item is used within a ScrollView, the content size is
47 automatically calculated based on the implicit size of its contained item.
48 However, if more than one item is used (or an implicit size is not
49 provided), the \l {QtQuick.Controls::Pane::}{contentWidth} and
50 \l {QtQuick.Controls::Pane::}{contentHeight} properties must
51 be set to the combined size of its contained items.
52 \li If the content size is less than or equal to the size of the ScrollView,
53 it will not be scrollable.
54 \li If you want the ScrollView to only scroll vertically, you can bind
55 \l {QtQuick.Controls::Pane::}{contentWidth} to
56 \l {QtQuick.Controls::Control::}{availableWidth}
57 (and vice versa for contentHeight). This will let the contents fill
58 out all the available space horizontally inside the ScrollView, taking
59 any padding or scroll bars into account.
60 \endlist
61
62 \section2 Scroll Bars
63
64 The horizontal and vertical scroll bars can be accessed and customized using
65 the \l {ScrollBar::horizontal}{ScrollBar.horizontal} and \l {ScrollBar::vertical}
66 {ScrollBar.vertical} attached properties. The following example adjusts the scroll
67 bar policies so that the horizontal scroll bar is always off, and the vertical
68 scroll bar is always on.
69
70 \snippet qtquickcontrols-scrollview-policy.qml file
71
72 \section2 Touch vs. Mouse Interaction
73
74 On touch, ScrollView enables flicking and makes the scroll bars non-interactive.
75
76 \image qtquickcontrols-scrollindicator.gif
77
78 When interacted with a mouse device, flicking is disabled and the scroll bars
79 are interactive.
80
81 \image qtquickcontrols-scrollbar.gif
82
83 Scroll bars can be made interactive on touch, or non-interactive when interacted
84 with a mouse device, by setting the \l {ScrollBar::}{interactive} property explicitly
85 to \c true or \c false, respectively.
86
87 \snippet qtquickcontrols-scrollview-interactive.qml file
88
89 \include varying-delegate-heights-section.qdocinc {file} {2} {ScrollBar}
90
91 \sa ScrollBar, ScrollIndicator, {Customizing ScrollView}, {Container Controls},
92 {Focus Management in Qt Quick Controls}
93*/
94
95class QQuickScrollViewPrivate : public QQuickPanePrivate
96{
97public:
98 Q_DECLARE_PUBLIC(QQuickScrollView)
99
100 QQmlListProperty<QObject> contentData() override;
101 QQmlListProperty<QQuickItem> contentChildren() override;
102 QList<QQuickItem *> contentChildItems() const override;
103 QQuickItem* getFirstChild() const override;
104
105 QQuickItem *getContentItem() override;
106
107 enum class ContentItemFlag {
108 DoNotSet,
109 Set
110 };
111
112 QQuickFlickable *ensureFlickable(ContentItemFlag contentItemFlag);
113 bool setFlickable(QQuickFlickable *flickable, ContentItemFlag contentItemFlag);
114
115 void flickableContentWidthChanged();
116 void flickableContentHeightChanged();
117
118 qreal getContentWidth() const override;
119 qreal getContentHeight() const override;
120
121 QQuickScrollBar *verticalScrollBar() const;
122 QQuickScrollBar *horizontalScrollBar() const;
123
124 void setScrollBarsInteractive(bool interactive);
125
126 static void contentData_append(QQmlListProperty<QObject> *prop, QObject *obj);
127 static qsizetype contentData_count(QQmlListProperty<QObject> *prop);
128 static QObject *contentData_at(QQmlListProperty<QObject> *prop, qsizetype index);
129 static void contentData_clear(QQmlListProperty<QObject> *prop);
130
131 static void contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *obj);
132 static qsizetype contentChildren_count(QQmlListProperty<QQuickItem> *prop);
133 static QQuickItem *contentChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index);
134 static void contentChildren_clear(QQmlListProperty<QQuickItem> *prop);
135
136 void itemImplicitWidthChanged(QQuickItem *item) override;
137
138 void updateScrollBarWidth();
139 void updateScrollBarHeight();
140
141 void disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar);
142 bool wasTouched = false;
143 QQuickFlickable *flickable = nullptr;
144 bool flickableHasExplicitContentWidth = true;
145 bool flickableHasExplicitContentHeight = true;
146 bool isUpdatingScrollBar = false;
147 qreal effectiveScrollBarWidth = 0;
148 qreal effectiveScrollBarHeight = 0;
149};
150
151QList<QQuickItem *> QQuickScrollViewPrivate::contentChildItems() const
152{
153 if (!flickable)
154 return QList<QQuickItem *>();
155
156 return flickable->contentItem()->childItems();
157}
158
159QQuickItem *QQuickScrollViewPrivate::getContentItem()
160{
161 if (!contentItem)
162 executeContentItem();
163 // This function is called by QQuickControl::contentItem() to lazily create
164 // a contentItem, so we don't need to try to set it again.
165 return ensureFlickable(contentItemFlag: ContentItemFlag::DoNotSet);
166}
167
168QQuickItem* QQuickScrollViewPrivate::getFirstChild() const
169{
170 return contentChildItems().value(i: 0);
171}
172
173QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable(ContentItemFlag contentItemFlag)
174{
175 Q_Q(QQuickScrollView);
176 if (!flickable) {
177 flickableHasExplicitContentWidth = false;
178 flickableHasExplicitContentHeight = false;
179 // Pass ourselves as the Flickable's parent item.
180 auto flickable = new QQuickFlickable(q);
181 // We almost always want to clip the flickable so that flickable
182 // contents doesn't show up outside the scrollview. The only time
183 // this is not really needed, is when the scrollview covers the whole
184 // window and the scrollbars are transient. But for that corner case, if this
185 // optimization is needed, the user can simply create his own flickable
186 // child inside the scrollview, and control clipping on it explicit.
187 flickable->setClip(true);
188 flickable->setPixelAligned(true);
189 setFlickable(flickable, contentItemFlag);
190 }
191 return flickable;
192}
193
194void QQuickScrollViewPrivate::updateScrollBarWidth()
195{
196 Q_Q(QQuickScrollView);
197 qreal oldEffectiveScrollBarWidth = effectiveScrollBarWidth;
198 if (auto *vBar = verticalScrollBar()) {
199 if (vBar->policy() == QQuickScrollBar::AlwaysOff || !vBar->isVisible())
200 effectiveScrollBarWidth = 0;
201 else
202 effectiveScrollBarWidth = vBar->width();
203 }
204 if (effectiveScrollBarWidth != oldEffectiveScrollBarWidth) {
205 if (!isUpdatingScrollBar) {
206 QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
207 emit q->effectiveScrollBarWidthChanged();
208 }
209 }
210}
211
212void QQuickScrollViewPrivate::updateScrollBarHeight()
213{
214 Q_Q(QQuickScrollView);
215 qreal oldEffectiveScrollBarHeight = effectiveScrollBarHeight;
216 if (auto *hBar = horizontalScrollBar()) {
217 if (hBar->policy() == QQuickScrollBar::AlwaysOff || !hBar->isVisible())
218 effectiveScrollBarHeight = 0;
219 else
220 effectiveScrollBarHeight = hBar->height();
221 }
222 if (effectiveScrollBarHeight != oldEffectiveScrollBarHeight) {
223 if (!isUpdatingScrollBar) {
224 QScopedValueRollback<bool> rollback(isUpdatingScrollBar, true);
225 emit q->effectiveScrollBarHeightChanged();
226 }
227
228 }
229}
230
231void QQuickScrollViewPrivate::disconnectScrollBarSignals(QQuickScrollBarAttachedPrivate *scrollBar)
232{
233 if (!scrollBar)
234 return;
235
236 if (scrollBar->vertical) {
237 QObjectPrivate::disconnect(sender: scrollBar->vertical, signal: &QQuickScrollBar::policyChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarWidth);
238 QObjectPrivate::disconnect(sender: scrollBar->vertical, signal: &QQuickScrollBar::visibleChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarWidth);
239 }
240 if (scrollBar->horizontal) {
241 QObjectPrivate::disconnect(sender: scrollBar->horizontal, signal: &QQuickScrollBar::policyChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarHeight);
242 QObjectPrivate::disconnect(sender: scrollBar->horizontal, signal: &QQuickScrollBar::visibleChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarHeight);
243 }
244}
245
246bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, ContentItemFlag contentItemFlag)
247{
248 Q_Q(QQuickScrollView);
249 if (item == flickable)
250 return false;
251
252 QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(object: qmlAttachedPropertiesObject<QQuickScrollBar>(obj: q, create: false));
253
254 if (flickable) {
255 flickable->removeEventFilter(obj: q);
256
257 if (attached) {
258 auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
259 scrollBar->setFlickable(nullptr);
260 disconnectScrollBarSignals(scrollBar);
261 }
262
263 QObjectPrivate::disconnect(sender: flickable->contentItem(), signal: &QQuickItem::childrenChanged, receiverPrivate: this, slot: &QQuickPanePrivate::contentChildrenChange);
264 QObjectPrivate::disconnect(sender: flickable, signal: &QQuickFlickable::contentWidthChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::flickableContentWidthChanged);
265 QObjectPrivate::disconnect(sender: flickable, signal: &QQuickFlickable::contentHeightChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::flickableContentHeightChanged);
266 }
267
268 flickable = item;
269 if (contentItemFlag == ContentItemFlag::Set)
270 q->setContentItem(flickable);
271
272 if (flickable) {
273 flickable->installEventFilter(filterObj: q);
274 if (hasContentWidth)
275 flickable->setContentWidth(contentWidth);
276 else
277 flickableContentWidthChanged();
278 if (hasContentHeight)
279 flickable->setContentHeight(contentHeight);
280 else
281 flickableContentHeightChanged();
282
283 if (attached) {
284 auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
285 scrollBar->setFlickable(flickable);
286 if (scrollBar->vertical) {
287 QObjectPrivate::connect(sender: scrollBar->vertical, signal: &QQuickScrollBar::policyChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarWidth);
288 QObjectPrivate::connect(sender: scrollBar->vertical, signal: &QQuickScrollBar::visibleChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarWidth);
289 }
290 if (scrollBar->horizontal) {
291 QObjectPrivate::connect(sender: scrollBar->horizontal, signal: &QQuickScrollBar::policyChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarHeight);
292 QObjectPrivate::connect(sender: scrollBar->horizontal, signal: &QQuickScrollBar::visibleChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::updateScrollBarHeight);
293 }
294 }
295
296 QObjectPrivate::connect(sender: flickable->contentItem(), signal: &QQuickItem::childrenChanged, receiverPrivate: this, slot: &QQuickPanePrivate::contentChildrenChange);
297 QObjectPrivate::connect(sender: flickable, signal: &QQuickFlickable::contentWidthChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::flickableContentWidthChanged);
298 QObjectPrivate::connect(sender: flickable, signal: &QQuickFlickable::contentHeightChanged, receiverPrivate: this, slot: &QQuickScrollViewPrivate::flickableContentHeightChanged);
299 }
300
301 return true;
302}
303
304void QQuickScrollViewPrivate::flickableContentWidthChanged()
305{
306 Q_Q(QQuickScrollView);
307 if (!flickable || !componentComplete)
308 return;
309
310 const qreal cw = flickable->contentWidth();
311 if (qFuzzyCompare(p1: cw, p2: implicitContentWidth))
312 return;
313
314 flickableHasExplicitContentWidth = true;
315 implicitContentWidth = cw;
316 emit q->implicitContentWidthChanged();
317}
318
319void QQuickScrollViewPrivate::flickableContentHeightChanged()
320{
321 Q_Q(QQuickScrollView);
322 if (!flickable || !componentComplete)
323 return;
324
325 const qreal ch = flickable->contentHeight();
326 if (qFuzzyCompare(p1: ch, p2: implicitContentHeight))
327 return;
328
329 flickableHasExplicitContentHeight = true;
330 implicitContentHeight = ch;
331 emit q->implicitContentHeightChanged();
332}
333
334qreal QQuickScrollViewPrivate::getContentWidth() const
335{
336 if (flickable && flickableHasExplicitContentWidth)
337 return flickable->contentWidth();
338
339 // The scrollview wraps a flickable created by us, and nobody searched for it and
340 // modified its contentWidth. In that case, since the application does not control
341 // this flickable, we fall back to calculate the content width based on the child
342 // items inside it.
343 return QQuickPanePrivate::getContentWidth();
344}
345
346qreal QQuickScrollViewPrivate::getContentHeight() const
347{
348 if (flickable && flickableHasExplicitContentHeight)
349 return flickable->contentHeight();
350
351 // The scrollview wraps a flickable created by us, and nobody searched for it and
352 // modified its contentHeight. In that case, since the application does not control
353 // this flickable, we fall back to calculate the content height based on the child
354 // items inside it.
355 return QQuickPanePrivate::getContentHeight();
356}
357
358QQuickScrollBar *QQuickScrollViewPrivate::verticalScrollBar() const
359{
360 Q_Q(const QQuickScrollView);
361 QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(object: qmlAttachedPropertiesObject<QQuickScrollBar>(obj: q, create: false));
362 if (!attached)
363 return nullptr;
364 return attached->vertical();
365}
366
367QQuickScrollBar *QQuickScrollViewPrivate::horizontalScrollBar() const
368{
369 Q_Q(const QQuickScrollView);
370 QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(object: qmlAttachedPropertiesObject<QQuickScrollBar>(obj: q, create: false));
371 if (!attached)
372 return nullptr;
373 return attached->horizontal();
374}
375
376void QQuickScrollViewPrivate::setScrollBarsInteractive(bool interactive)
377{
378 QQuickScrollBar *hbar = horizontalScrollBar();
379 if (hbar) {
380 QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(bar: hbar);
381 if (!p->explicitInteractive)
382 p->setInteractive(interactive);
383 }
384
385 QQuickScrollBar *vbar = verticalScrollBar();
386 if (vbar) {
387 QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(bar: vbar);
388 if (!p->explicitInteractive)
389 p->setInteractive(interactive);
390 }
391}
392
393void QQuickScrollViewPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj)
394{
395 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
396 // If we don't yet have a flickable assigned, and this object is a Flickable,
397 // make it our contentItem.
398 if (!p->flickable && p->setFlickable(item: qobject_cast<QQuickFlickable *>(object: obj), contentItemFlag: ContentItemFlag::Set))
399 return;
400
401 QQuickFlickable *flickable = p->ensureFlickable(contentItemFlag: ContentItemFlag::Set);
402 Q_ASSERT(flickable);
403 // Add the object that was declared as a child of us as a child object of the Flickable.
404 QQmlListProperty<QObject> data = flickable->flickableData();
405 data.append(&data, obj);
406}
407
408qsizetype QQuickScrollViewPrivate::contentData_count(QQmlListProperty<QObject> *prop)
409{
410 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
411 if (!p->flickable)
412 return 0;
413
414 QQmlListProperty<QObject> data = p->flickable->flickableData();
415 return data.count(&data);
416}
417
418QObject *QQuickScrollViewPrivate::contentData_at(QQmlListProperty<QObject> *prop, qsizetype index)
419{
420 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
421 if (!p->flickable)
422 return nullptr;
423
424 QQmlListProperty<QObject> data = p->flickable->flickableData();
425 return data.at(&data, index);
426}
427
428void QQuickScrollViewPrivate::contentData_clear(QQmlListProperty<QObject> *prop)
429{
430 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
431 if (!p->flickable)
432 return;
433
434 QQmlListProperty<QObject> data = p->flickable->flickableData();
435 return data.clear(&data);
436}
437
438void QQuickScrollViewPrivate::contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *item)
439{
440 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
441 if (!p->flickable)
442 p->setFlickable(item: qobject_cast<QQuickFlickable *>(object: item), contentItemFlag: ContentItemFlag::Set);
443
444 QQuickFlickable *flickable = p->ensureFlickable(contentItemFlag: ContentItemFlag::Set);
445 Q_ASSERT(flickable);
446 // Add the item that was declared as a child of us as a child item of the Flickable's contentItem.
447 QQmlListProperty<QQuickItem> children = flickable->flickableChildren();
448 children.append(&children, item);
449}
450
451qsizetype QQuickScrollViewPrivate::contentChildren_count(QQmlListProperty<QQuickItem> *prop)
452{
453 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
454 if (!p->flickable)
455 return 0;
456
457 QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren();
458 return children.count(&children);
459}
460
461QQuickItem *QQuickScrollViewPrivate::contentChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
462{
463 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
464 if (!p->flickable)
465 return nullptr;
466
467 QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren();
468 return children.at(&children, index);
469}
470
471void QQuickScrollViewPrivate::contentChildren_clear(QQmlListProperty<QQuickItem> *prop)
472{
473 QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data);
474 if (!p->flickable)
475 return;
476
477 QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren();
478 children.clear(&children);
479}
480
481void QQuickScrollViewPrivate::itemImplicitWidthChanged(QQuickItem *item)
482{
483 // a special case for width<->height dependent content (wrapping text) in ScrollView
484 if (contentWidth < 0 && !componentComplete)
485 return;
486
487 QQuickPanePrivate::itemImplicitWidthChanged(item);
488}
489
490QQuickScrollView::QQuickScrollView(QQuickItem *parent)
491 : QQuickPane(*(new QQuickScrollViewPrivate), parent)
492{
493 Q_D(QQuickScrollView);
494 d->contentWidth = -1;
495 d->contentHeight = -1;
496
497 setFiltersChildMouseEvents(true);
498 setWheelEnabled(true);
499}
500
501QQuickScrollView::~QQuickScrollView()
502{
503 Q_D(QQuickScrollView);
504 QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(object: qmlAttachedPropertiesObject<QQuickScrollBar>(obj: this, create: false));
505 if (attached) {
506 auto *scrollBar = QQuickScrollBarAttachedPrivate::get(attached);
507 d->disconnectScrollBarSignals(scrollBar);
508 }
509}
510
511/*!
512 \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarWidth
513 \since 6.6
514
515 This property holds the effective width of the vertical scrollbar.
516 When the scrollbar is visible, this property is the current width of the
517 scrollbar. When the scroll bar is not visible or its policy is set to
518 \c QQuickScrollBar::AlwaysOff, this property is \c 0.
519
520 \sa {ScrollBar::policy}
521*/
522qreal QQuickScrollView::effectiveScrollBarWidth()
523{
524 Q_D(QQuickScrollView);
525 return d->effectiveScrollBarWidth;
526}
527
528/*!
529 \qmlproperty real QtQuick.Controls::ScrollView::effectiveScrollBarHeight
530 \since 6.6
531
532 This property holds the effective height of the horizontal scrollbar.
533 When the scrollbar is visible, this property is the current height of
534 the scrollbar. When the scroll bar is not visible or its policy is set
535 to \c QQuickScrollBar::AlwaysOff, this property is \c 0.
536
537 \sa {ScrollBar::policy}
538*/
539qreal QQuickScrollView::effectiveScrollBarHeight()
540{
541 Q_D(QQuickScrollView);
542 return d->effectiveScrollBarHeight;
543}
544
545/*!
546 \qmlproperty list<QtObject> QtQuick.Controls::ScrollView::contentData
547 \qmldefault
548
549 This property holds the list of content data.
550
551 The list contains all objects that have been declared in QML as children of the view.
552
553 \note Unlike \c contentChildren, \c contentData does include non-visual QML objects.
554
555 \sa Item::data, contentChildren
556*/
557QQmlListProperty<QObject> QQuickScrollViewPrivate::contentData()
558{
559 Q_Q(QQuickScrollView);
560 return QQmlListProperty<QObject>(q, this,
561 QQuickScrollViewPrivate::contentData_append,
562 QQuickScrollViewPrivate::contentData_count,
563 QQuickScrollViewPrivate::contentData_at,
564 QQuickScrollViewPrivate::contentData_clear);
565}
566
567/*!
568 \qmlproperty list<Item> QtQuick.Controls::ScrollView::contentChildren
569
570 This property holds the list of content children.
571
572 The list contains all items that have been declared in QML as children of the view.
573
574 \note Unlike \c contentData, \c contentChildren does not include non-visual QML objects.
575
576 \sa Item::children, contentData
577*/
578QQmlListProperty<QQuickItem> QQuickScrollViewPrivate::contentChildren()
579{
580 Q_Q(QQuickScrollView);
581 return QQmlListProperty<QQuickItem>(q, this,
582 QQuickScrollViewPrivate::contentChildren_append,
583 QQuickScrollViewPrivate::contentChildren_count,
584 QQuickScrollViewPrivate::contentChildren_at,
585 QQuickScrollViewPrivate::contentChildren_clear);
586}
587
588bool QQuickScrollView::childMouseEventFilter(QQuickItem *item, QEvent *event)
589{
590 Q_D(QQuickScrollView);
591 switch (event->type()) {
592 case QEvent::TouchBegin:
593 d->wasTouched = true;
594 d->setScrollBarsInteractive(false);
595 return false;
596
597 case QEvent::TouchEnd:
598 d->wasTouched = false;
599 return false;
600
601 case QEvent::MouseButtonPress:
602 // NOTE: Flickable does not handle touch events, only synthesized mouse events
603 if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) {
604 d->wasTouched = false;
605 d->setScrollBarsInteractive(true);
606 return false;
607 }
608 return !d->wasTouched && item == d->flickable;
609
610 case QEvent::MouseMove:
611 case QEvent::MouseButtonRelease:
612 if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized)
613 return item == d->flickable;
614 break;
615
616 case QEvent::HoverEnter:
617 case QEvent::HoverMove:
618 if (d->wasTouched && (item == d->verticalScrollBar() || item == d->horizontalScrollBar()))
619 d->setScrollBarsInteractive(true);
620 break;
621
622 default:
623 break;
624 }
625
626 return false;
627}
628
629bool QQuickScrollView::eventFilter(QObject *object, QEvent *event)
630{
631 Q_D(QQuickScrollView);
632 if (event->type() == QEvent::Wheel) {
633 d->setScrollBarsInteractive(true);
634 if (!d->wheelEnabled) {
635 event->ignore();
636 return true;
637 }
638 }
639 return QQuickPane::eventFilter(watched: object, event);
640}
641
642void QQuickScrollView::keyPressEvent(QKeyEvent *event)
643{
644 Q_D(QQuickScrollView);
645 QQuickPane::keyPressEvent(event);
646 switch (event->key()) {
647 case Qt::Key_Up:
648 if (QQuickScrollBar *vbar = d->verticalScrollBar()) {
649 vbar->decrease();
650 event->accept();
651 }
652 break;
653 case Qt::Key_Down:
654 if (QQuickScrollBar *vbar = d->verticalScrollBar()) {
655 vbar->increase();
656 event->accept();
657 }
658 break;
659 case Qt::Key_Left:
660 if (QQuickScrollBar *hbar = d->horizontalScrollBar()) {
661 hbar->decrease();
662 event->accept();
663 }
664 break;
665 case Qt::Key_Right:
666 if (QQuickScrollBar *hbar = d->horizontalScrollBar()) {
667 hbar->increase();
668 event->accept();
669 }
670 break;
671 default:
672 event->ignore();
673 break;
674 }
675}
676
677void QQuickScrollView::componentComplete()
678{
679 Q_D(QQuickScrollView);
680 QQuickPane::componentComplete();
681 if (!d->contentItem)
682 d->ensureFlickable(contentItemFlag: QQuickScrollViewPrivate::ContentItemFlag::Set);
683}
684
685void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
686{
687 Q_D(QQuickScrollView);
688 if (newItem != d->flickable) {
689 // The new flickable was not created by us. In that case, we always
690 // assume/require that it has an explicit content size assigned.
691 d->flickableHasExplicitContentWidth = true;
692 d->flickableHasExplicitContentHeight = true;
693 auto newItemAsFlickable = qobject_cast<QQuickFlickable *>(object: newItem);
694 if (newItem && !newItemAsFlickable)
695 qmlWarning(me: this) << "ScrollView only supports Flickable types as its contentItem";
696 // This is called by QQuickControlPrivate::setContentItem_helper, so no need to
697 // try to set it as the contentItem.
698 d->setFlickable(item: newItemAsFlickable, contentItemFlag: QQuickScrollViewPrivate::ContentItemFlag::DoNotSet);
699 // We do, however, need to set us as its parent item, as setContentItem_helper will only
700 // do so if the item doesn't already have a parent. If newItem wasn't declared as our
701 // child and was instead imperatively assigned, it may already have a parent item,
702 // which we'll need to override.
703 if (newItem) {
704 newItem->setParentItem(this);
705
706 // Make sure that the scroll bars are stacked in front of the flickable,
707 // otherwise events won't get through to them.
708 QQuickScrollBar *verticalBar = d->verticalScrollBar();
709 if (verticalBar)
710 verticalBar->stackAfter(newItem);
711 QQuickScrollBar *horizontalBar = d->horizontalScrollBar();
712 if (horizontalBar)
713 horizontalBar->stackAfter(newItem);
714 }
715 }
716 QQuickPane::contentItemChange(newItem, oldItem);
717}
718
719void QQuickScrollView::contentSizeChange(const QSizeF &newSize, const QSizeF &oldSize)
720{
721 Q_D(QQuickScrollView);
722 QQuickPane::contentSizeChange(newSize, oldSize);
723 if (d->flickable) {
724 // Only set the content size on the flickable if the flickable doesn't
725 // have an explicit assignment from before. Otherwise we can end up overwriting
726 // assignments done to those properties by the application. The
727 // exception is if the application has assigned a content size
728 // directly to the scrollview, which will then win even if the
729 // application has assigned something else to the flickable.
730 if (d->hasContentWidth || !d->flickableHasExplicitContentWidth) {
731 d->flickable->setContentWidth(newSize.width());
732 d->updateScrollBarWidth();
733 }
734 if (d->hasContentHeight || !d->flickableHasExplicitContentHeight) {
735 d->flickable->setContentHeight(newSize.height());
736 d->updateScrollBarHeight();
737 }
738 }
739}
740
741#if QT_CONFIG(accessibility)
742QAccessible::Role QQuickScrollView::accessibleRole() const
743{
744 return QAccessible::Pane;
745}
746#endif
747
748QT_END_NAMESPACE
749
750#include "moc_qquickscrollview_p.cpp"
751

source code of qtdeclarative/src/quicktemplates/qquickscrollview.cpp