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

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