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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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