1// Copyright (C) 2017 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 "qquickscrollbar_p.h"
5#include "qquickscrollbar_p_p.h"
6#include "qquickscrollview_p.h"
7
8#include <QtQml/qqmlinfo.h>
9#include <QtQuick/private/qquickflickable_p.h>
10#if QT_CONFIG(accessibility)
11#include <QtQuick/private/qquickaccessibleattached_p.h>
12#endif
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \qmltype ScrollBar
18 \inherits Control
19//! \instantiates QQuickScrollBar
20 \inqmlmodule QtQuick.Controls
21 \since 5.7
22 \ingroup qtquickcontrols-indicators
23 \brief Vertical or horizontal interactive scroll bar.
24
25 \image qtquickcontrols-scrollbar.gif
26
27 ScrollBar is an interactive bar that can be used to scroll to a specific
28 position. A scroll bar can be either \l vertical or \l horizontal, and can
29 be attached to any \l Flickable, such as \l ListView and \l GridView.
30 It can also be used with \l ScrollView.
31
32 \code
33 Flickable {
34 // ...
35 ScrollBar.vertical: ScrollBar { }
36 }
37 \endcode
38
39 \section1 Attaching ScrollBar to a Flickable
40
41 When ScrollBar is attached \l {ScrollBar::vertical}{vertically} or
42 \l {ScrollBar::horizontal}{horizontally} to a Flickable, its geometry and
43 the following properties are automatically set and updated as appropriate:
44
45 \list
46 \li \l orientation
47 \li \l position
48 \li \l {ScrollBar::} {size}
49 \li \l active
50 \endlist
51
52 An attached ScrollBar re-parents itself to the target Flickable. A vertically
53 attached ScrollBar resizes itself to the height of the Flickable, and positions
54 itself to either side of it based on the \l {Control::mirrored}{layout direction}.
55 A horizontally attached ScrollBar resizes itself to the width of the Flickable,
56 and positions itself to the bottom. The automatic geometry management can be disabled
57 by specifying another parent for the attached ScrollBar. This can be useful, for
58 example, if the ScrollBar should be placed outside a clipping Flickable. This is
59 demonstrated by the following example:
60
61 \code
62 Flickable {
63 id: flickable
64 clip: true
65 // ...
66 ScrollBar.vertical: ScrollBar {
67 parent: flickable.parent
68 anchors.top: flickable.top
69 anchors.left: flickable.right
70 anchors.bottom: flickable.bottom
71 }
72 }
73 \endcode
74
75 Notice that ScrollBar does not filter key events of the Flickable it is
76 attached to. The following example illustrates how to implement scrolling
77 with up and down keys:
78
79 \code
80 Flickable {
81 focus: true
82
83 Keys.onUpPressed: scrollBar.decrease()
84 Keys.onDownPressed: scrollBar.increase()
85
86 ScrollBar.vertical: ScrollBar { id: scrollBar }
87 }
88 \endcode
89
90 \section1 Binding the Active State of Horizontal and Vertical Scroll Bars
91
92 Horizontal and vertical scroll bars do not share the \l active state with
93 each other by default. In order to keep both bars visible whilst scrolling
94 to either direction, establish a two-way binding between the active states
95 as presented by the following example:
96
97 \snippet qtquickcontrols-scrollbar-active.qml 1
98
99 \section1 Non-attached Scroll Bars
100
101 It is possible to create an instance of ScrollBar without using the
102 attached property API. This is useful when the behavior of the attached
103 scroll bar is not sufficient or a \l Flickable is not in use. In the
104 following example, horizontal and vertical scroll bars are used to
105 scroll over the text without using \l Flickable:
106
107 \snippet qtquickcontrols-scrollbar-non-attached.qml 1
108
109 \image qtquickcontrols-scrollbar-non-attached.png
110
111 When using a non-attached ScrollBar, the following must be done manually:
112
113 \list
114 \li Layout the scroll bar (with the \l {Item::}{x} and \l {Item::}{y} or
115 \l {Item::}{anchors} property, for example).
116 \li Set the \l size and \l position properties to determine the size and position
117 of the scroll bar in relation to the scrolled item.
118 \li Set the \l active property to determine when the scroll bar will be
119 visible.
120 \endlist
121
122 \sa ScrollIndicator, ScrollView, {Customizing ScrollBar}, {Indicator Controls}
123*/
124
125static const QQuickItemPrivate::ChangeTypes QsbChangeTypes = QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed;
126static const QQuickItemPrivate::ChangeTypes QsbHorizontalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitHeight;
127static const QQuickItemPrivate::ChangeTypes QsbVerticalChangeTypes = QsbChangeTypes | QQuickItemPrivate::ImplicitWidth;
128
129QQuickScrollBarPrivate::VisualArea QQuickScrollBarPrivate::visualArea() const
130{
131 qreal visualPos = position;
132
133 if (minimumSize > size && size != 1.0)
134 visualPos = position / (1.0 - size) * (1.0 - minimumSize);
135
136 qreal maximumSize = qMax<qreal>(a: 0.0, b: 1.0 - visualPos);
137 qreal visualSize = qMax<qreal>(a: minimumSize,
138 b: qMin<qreal>(a: qMax(a: size, b: minimumSize) + qMin<qreal>(a: 0, b: visualPos),
139 b: maximumSize));
140
141 visualPos = qMax<qreal>(a: 0,b: qMin<qreal>(a: visualPos,b: qMax<qreal>(a: 0, b: 1.0 - visualSize)));
142
143 return VisualArea(visualPos, visualSize);
144}
145
146qreal QQuickScrollBarPrivate::logicalPosition(qreal position) const
147{
148 if (minimumSize > size && minimumSize != 1.0)
149 return position * (1.0 - size) / (1.0 - minimumSize);
150 return position;
151}
152
153qreal QQuickScrollBarPrivate::snapPosition(qreal position) const
154{
155 const qreal effectiveStep = stepSize * (1.0 - size);
156 if (qFuzzyIsNull(d: effectiveStep))
157 return position;
158
159 return qRound(d: position / effectiveStep) * effectiveStep;
160}
161
162qreal QQuickScrollBarPrivate::positionAt(const QPointF &point) const
163{
164 Q_Q(const QQuickScrollBar);
165 if (orientation == Qt::Horizontal)
166 return logicalPosition(position: point.x() - q->leftPadding()) / q->availableWidth();
167 else
168 return logicalPosition(position: point.y() - q->topPadding()) / q->availableHeight();
169}
170
171void QQuickScrollBarPrivate::setInteractive(bool enabled)
172{
173 Q_Q(QQuickScrollBar);
174 if (interactive == enabled)
175 return;
176
177 interactive = enabled;
178 if (interactive) {
179 q->setAcceptedMouseButtons(Qt::LeftButton);
180#if QT_CONFIG(quicktemplates2_multitouch)
181 q->setAcceptTouchEvents(true);
182#endif
183#if QT_CONFIG(cursor)
184 q->setCursor(Qt::ArrowCursor);
185#endif
186 } else {
187 q->setAcceptedMouseButtons(Qt::NoButton);
188#if QT_CONFIG(quicktemplates2_multitouch)
189 q->setAcceptTouchEvents(false);
190#endif
191#if QT_CONFIG(cursor)
192 q->unsetCursor();
193#endif
194 q->ungrabMouse();
195 }
196 emit q->interactiveChanged();
197}
198
199void QQuickScrollBarPrivate::updateActive()
200{
201 Q_Q(QQuickScrollBar);
202#if QT_CONFIG(quicktemplates2_hover)
203 bool hover = hovered;
204#else
205 bool hover = false;
206#endif
207 q->setActive(moving || (interactive && (pressed || hover)));
208}
209
210void QQuickScrollBarPrivate::resizeContent()
211{
212 Q_Q(QQuickScrollBar);
213 if (!contentItem)
214 return;
215
216 // - negative overshoot (pos < 0): clamp the pos to 0, and deduct the overshoot from the size
217 // - positive overshoot (pos + size > 1): clamp the size to 1-pos
218 const VisualArea visual = visualArea();
219
220 if (orientation == Qt::Horizontal) {
221 contentItem->setPosition(QPointF(q->leftPadding() + visual.position * q->availableWidth(), q->topPadding()));
222 contentItem->setSize(QSizeF(q->availableWidth() * visual.size, q->availableHeight()));
223 } else {
224 contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + visual.position * q->availableHeight()));
225 contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * visual.size));
226 }
227}
228
229void QQuickScrollBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
230{
231 Q_Q(QQuickScrollBar);
232 QQuickControlPrivate::itemImplicitWidthChanged(item);
233 QQuickIndicatorButton *indicatorButton = q->decreaseVisual();
234 if (!indicatorButton || item != indicatorButton->indicator()) {
235 indicatorButton = q->increaseVisual();
236 if (!indicatorButton || item != indicatorButton->indicator())
237 return;
238 }
239 if (indicatorButton)
240 emit indicatorButton->implicitIndicatorWidthChanged();
241}
242
243void QQuickScrollBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
244{
245 Q_Q(QQuickScrollBar);
246 QQuickControlPrivate::itemImplicitHeightChanged(item);
247 QQuickIndicatorButton *indicatorButton = q->decreaseVisual();
248 if (!indicatorButton || item != indicatorButton->indicator()) {
249 indicatorButton = q->increaseVisual();
250 if (!indicatorButton || item != indicatorButton->indicator())
251 return;
252 }
253 if (indicatorButton)
254 emit indicatorButton->implicitIndicatorHeightChanged();
255}
256
257bool QQuickScrollBarPrivate::handlePress(const QPointF &point, ulong timestamp)
258{
259 Q_Q(QQuickScrollBar);
260 QQuickControlPrivate::handlePress(point, timestamp);
261 if (QQuickIndicatorButton *indicatorButton = q->decreaseVisual()) {
262 QQuickItem *decreaseArrow = indicatorButton->indicator();
263 if (decreaseArrow && decreaseArrow->contains(point: q->mapToItem(item: decreaseArrow, point: point + QPointF(0.5, 0.5)))) {
264 indicatorButton->setPressed(true);
265 q->decrease();
266 return true;
267 }
268 }
269
270 if (QQuickIndicatorButton *increaseObject = q->increaseVisual()) {
271 QQuickItem *increaseArrow = increaseObject->indicator();
272 if (increaseArrow && increaseArrow->contains(point: q->mapToItem(item: increaseArrow, point: point + QPointF(0.5, 0.5)))) {
273 increaseObject->setPressed(true);
274 q->increase();
275 return true;
276 }
277 }
278
279 offset = positionAt(point) - position;
280 qreal sz = qMax(a: size, b: logicalPosition(position: minimumSize));
281 if (offset < 0 || offset > sz)
282 offset = sz / 2;
283 q->setPressed(true);
284 return true;
285}
286
287bool QQuickScrollBarPrivate::handleMove(const QPointF &point, ulong timestamp)
288{
289 Q_Q(QQuickScrollBar);
290 QQuickControlPrivate::handleMove(point, timestamp);
291
292 /*
293 * handleMove() will be called as soon as you hold the mouse button down *anywhere* on the
294 * ScrollBar, including the increase/decrease button indicator areas. So without the following
295 * early return, it would move the scrollbar handle to one of its extremeties. That would
296 * ruin the behavior we would like when clicking e.g. the "increase button": To step the
297 * scrollbar gently.
298 */
299 if (!pressed)
300 return true;
301
302 qreal pos = qMax<qreal>(a: 0.0, b: qMin<qreal>(a: positionAt(point) - offset, b: 1.0 - size));
303 if (snapMode == QQuickScrollBar::SnapAlways)
304 pos = snapPosition(position: pos);
305 q->setPosition(pos);
306 return true;
307}
308
309bool QQuickScrollBarPrivate::handleRelease(const QPointF &point, ulong timestamp)
310{
311 Q_Q(QQuickScrollBar);
312 QQuickControlPrivate::handleRelease(point, timestamp);
313
314 if (orientation == Qt::Vertical) {
315 if (point.y() < q->topPadding() || point.y() >= (q->height() - q->bottomPadding()))
316 return true;
317 } else /* orientation == Qt::Horizontal */{
318 if (point.x() < q->leftPadding() || point.x() >= (q->width() - q->rightPadding()))
319 return true;
320 }
321
322 qreal pos = qMax<qreal>(a: 0.0, b: qMin<qreal>(a: positionAt(point) - offset, b: 1.0 - size));
323 if (snapMode != QQuickScrollBar::NoSnap)
324 pos = snapPosition(position: pos);
325 q->setPosition(pos);
326 offset = 0.0;
327 q->setPressed(false);
328 return true;
329}
330
331void QQuickScrollBarPrivate::handleUngrab()
332{
333 Q_Q(QQuickScrollBar);
334 QQuickControlPrivate::handleUngrab();
335 offset = 0.0;
336 q->setPressed(false);
337}
338
339void QQuickScrollBarPrivate::visualAreaChange(const VisualArea &newVisualArea, const VisualArea &oldVisualArea)
340{
341 Q_Q(QQuickScrollBar);
342 if (!qFuzzyCompare(p1: newVisualArea.size, p2: oldVisualArea.size))
343 emit q->visualSizeChanged();
344 if (!qFuzzyCompare(p1: newVisualArea.position, p2: oldVisualArea.position))
345 emit q->visualPositionChanged();
346}
347
348void QQuickScrollBarPrivate::updateHover(const QPointF &pos, std::optional<bool> newHoverState)
349{
350 Q_Q(QQuickScrollBar);
351 auto updateHoverOnButton = [&](QQuickIndicatorButton *sbButton) {
352 if (sbButton) {
353 bool hovered = newHoverState.value_or(u: false);
354 if (!newHoverState.has_value()) {
355 if (QQuickItem *indicator = sbButton->indicator())
356 hovered = indicator->contains(point: q->mapToItem(item: indicator, point: pos));
357 }
358 sbButton->setHovered(hovered);
359 }
360 };
361 updateHoverOnButton(q->decreaseVisual());
362 updateHoverOnButton(q->increaseVisual());
363}
364
365QQuickScrollBar::QQuickScrollBar(QQuickItem *parent)
366 : QQuickControl(*(new QQuickScrollBarPrivate), parent)
367{
368 Q_D(QQuickScrollBar);
369 d->decreaseVisual = new QQuickIndicatorButton(this);
370 d->increaseVisual = new QQuickIndicatorButton(this);
371 setKeepMouseGrab(true);
372 setAcceptedMouseButtons(Qt::LeftButton);
373#if QT_CONFIG(quicktemplates2_multitouch)
374 setAcceptTouchEvents(true);
375#endif
376#if QT_CONFIG(cursor)
377 setCursor(Qt::ArrowCursor);
378#endif
379}
380
381QQuickScrollBarAttached *QQuickScrollBar::qmlAttachedProperties(QObject *object)
382{
383 return new QQuickScrollBarAttached(object);
384}
385
386/*!
387 \qmlproperty real QtQuick.Controls::ScrollBar::size
388
389 This property holds the size of the scroll bar, scaled to \c {0.0 - 1.0}.
390
391 \sa {Flickable::visibleArea.heightRatio}{Flickable::visibleArea}
392
393 This property is automatically set when the scroll bar is
394 \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
395
396 \sa minimumSize, visualSize
397*/
398qreal QQuickScrollBar::size() const
399{
400 Q_D(const QQuickScrollBar);
401 return d->size;
402}
403
404void QQuickScrollBar::setSize(qreal size)
405{
406 Q_D(QQuickScrollBar);
407 if (!qt_is_finite(d: size))
408 return;
409 size = qBound(min: 0.0, val: size, max: 1.0);
410 if (qFuzzyCompare(p1: d->size, p2: size))
411 return;
412
413 const auto oldVisualArea = d->visualArea();
414 d->size = size;
415 if (d->size + d->position > 1.0) {
416 d->setPosition(position: 1.0 - d->size, notifyVisualChange: false);
417 }
418
419 if (isComponentComplete())
420 d->resizeContent();
421 emit sizeChanged();
422 d->visualAreaChange(newVisualArea: d->visualArea(), oldVisualArea);
423}
424
425/*!
426 \qmlproperty real QtQuick.Controls::ScrollBar::position
427
428 This property holds the position of the scroll bar, scaled to \c {0.0 - 1.0}.
429
430 The largest valid scrollbar position is \c {(1.0 - size)}. This gives
431 correct behavior for the most used case where moving the scrollbar
432 to the end will put the end of the document at the lower end of the
433 visible area of the connected Flickable.
434
435 \sa {Flickable::visibleArea.yPosition}{Flickable::visibleArea}
436
437 This property is automatically set when the scroll bar is
438 \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
439
440 \sa visualPosition
441*/
442qreal QQuickScrollBar::position() const
443{
444 Q_D(const QQuickScrollBar);
445 return d->position;
446}
447
448void QQuickScrollBar::setPosition(qreal position)
449{
450 Q_D(QQuickScrollBar);
451 d->setPosition(position);
452}
453
454void QQuickScrollBarPrivate::setPosition(qreal newPosition, bool notifyVisualChange)
455{
456 Q_Q(QQuickScrollBar);
457 if (!qt_is_finite(d: newPosition) || qFuzzyCompare(p1: position, p2: newPosition))
458 return;
459
460 auto oldVisualArea = visualArea();
461 position = newPosition;
462 if (q->isComponentComplete())
463 resizeContent();
464 emit q->positionChanged();
465 if (notifyVisualChange)
466 visualAreaChange(newVisualArea: visualArea(), oldVisualArea);
467}
468
469/*!
470 \qmlproperty real QtQuick.Controls::ScrollBar::stepSize
471
472 This property holds the step size. The default value is \c 0.0.
473
474 \sa snapMode, increase(), decrease()
475*/
476qreal QQuickScrollBar::stepSize() const
477{
478 Q_D(const QQuickScrollBar);
479 return d->stepSize;
480}
481
482void QQuickScrollBar::setStepSize(qreal step)
483{
484 Q_D(QQuickScrollBar);
485 if (!qt_is_finite(d: step) || qFuzzyCompare(p1: d->stepSize, p2: step))
486 return;
487
488 d->stepSize = step;
489 emit stepSizeChanged();
490}
491
492/*!
493 \qmlproperty bool QtQuick.Controls::ScrollBar::active
494
495 This property holds whether the scroll bar is active, i.e. when it's \l pressed
496 or the attached Flickable is \l {Flickable::moving}{moving}.
497
498 It is possible to keep \l {Binding the Active State of Horizontal and Vertical Scroll Bars}
499 {both horizontal and vertical bars visible} while scrolling in either direction.
500
501 This property is automatically set when the scroll bar is
502 \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
503*/
504bool QQuickScrollBar::isActive() const
505{
506 Q_D(const QQuickScrollBar);
507 return d->active;
508}
509
510void QQuickScrollBar::setActive(bool active)
511{
512 Q_D(QQuickScrollBar);
513 if (d->active == active)
514 return;
515
516 d->active = active;
517 emit activeChanged();
518}
519
520/*!
521 \qmlproperty bool QtQuick.Controls::ScrollBar::pressed
522
523 This property holds whether the scroll bar is pressed.
524*/
525bool QQuickScrollBar::isPressed() const
526{
527 Q_D(const QQuickScrollBar);
528 return d->pressed;
529}
530
531void QQuickScrollBar::setPressed(bool pressed)
532{
533 Q_D(QQuickScrollBar);
534 if (!pressed) {
535 if (QQuickIndicatorButton *button = decreaseVisual())
536 button->setPressed(false);
537 if (QQuickIndicatorButton *button = increaseVisual())
538 button->setPressed(false);
539 }
540 if (d->pressed == pressed)
541 return;
542
543 d->pressed = pressed;
544 setAccessibleProperty(propertyName: "pressed", value: pressed);
545 d->updateActive();
546 emit pressedChanged();
547}
548
549/*!
550 \qmlproperty enumeration QtQuick.Controls::ScrollBar::orientation
551
552 This property holds the orientation of the scroll bar.
553
554 Possible values:
555 \value Qt.Horizontal Horizontal
556 \value Qt.Vertical Vertical (default)
557
558 This property is automatically set when the scroll bar is
559 \l {Attaching ScrollBar to a Flickable}{attached to a flickable}.
560
561 \sa horizontal, vertical
562*/
563Qt::Orientation QQuickScrollBar::orientation() const
564{
565 Q_D(const QQuickScrollBar);
566 return d->orientation;
567}
568
569void QQuickScrollBar::setOrientation(Qt::Orientation orientation)
570{
571 Q_D(QQuickScrollBar);
572 if (d->orientation == orientation)
573 return;
574
575 d->orientation = orientation;
576 if (isComponentComplete())
577 d->resizeContent();
578 emit orientationChanged();
579}
580
581/*!
582 \since QtQuick.Controls 2.2 (Qt 5.9)
583 \qmlproperty enumeration QtQuick.Controls::ScrollBar::snapMode
584
585 This property holds the snap mode.
586
587 Possible values:
588 \value ScrollBar.NoSnap The scrollbar does not snap (default).
589 \value ScrollBar.SnapAlways The scrollbar snaps while dragged.
590 \value ScrollBar.SnapOnRelease The scrollbar does not snap while being dragged, but only after released.
591
592 In the following table, the various modes are illustrated with animations.
593 The movement and the \l stepSize (\c 0.25) are identical in each animation.
594
595 \table
596 \header
597 \row \li \b Value \li \b Example
598 \row \li \c ScrollBar.NoSnap \li \image qtquickcontrols-scrollbar-nosnap.gif
599 \row \li \c ScrollBar.SnapAlways \li \image qtquickcontrols-scrollbar-snapalways.gif
600 \row \li \c ScrollBar.SnapOnRelease \li \image qtquickcontrols-scrollbar-snaponrelease.gif
601 \endtable
602
603 \sa stepSize
604*/
605QQuickScrollBar::SnapMode QQuickScrollBar::snapMode() const
606{
607 Q_D(const QQuickScrollBar);
608 return d->snapMode;
609}
610
611void QQuickScrollBar::setSnapMode(SnapMode mode)
612{
613 Q_D(QQuickScrollBar);
614 if (d->snapMode == mode)
615 return;
616
617 d->snapMode = mode;
618 emit snapModeChanged();
619}
620
621/*!
622 \since QtQuick.Controls 2.2 (Qt 5.9)
623 \qmlproperty bool QtQuick.Controls::ScrollBar::interactive
624
625 This property holds whether the scroll bar is interactive. The default value is \c true.
626
627 A non-interactive scroll bar is visually and behaviorally similar to \l ScrollIndicator.
628 This property is useful for switching between typical mouse- and touch-orientated UIs
629 with interactive and non-interactive scroll bars, respectively.
630*/
631bool QQuickScrollBar::isInteractive() const
632{
633 Q_D(const QQuickScrollBar);
634 return d->interactive;
635}
636
637void QQuickScrollBar::setInteractive(bool interactive)
638{
639 Q_D(QQuickScrollBar);
640 d->explicitInteractive = true;
641 d->setInteractive(interactive);
642}
643
644void QQuickScrollBar::resetInteractive()
645{
646 Q_D(QQuickScrollBar);
647 d->explicitInteractive = false;
648 d->setInteractive(true);
649}
650
651/*!
652 \since QtQuick.Controls 2.2 (Qt 5.9)
653 \qmlproperty enumeration QtQuick.Controls::ScrollBar::policy
654
655 This property holds the policy of the scroll bar. The default policy is \c ScrollBar.AsNeeded.
656
657 Possible values:
658 \value ScrollBar.AsNeeded The scroll bar is only shown when the content is too large to fit.
659 \value ScrollBar.AlwaysOff The scroll bar is never shown.
660 \value ScrollBar.AlwaysOn The scroll bar is always shown.
661
662 The following example keeps the vertical scroll bar always visible:
663
664 \snippet qtquickcontrols-scrollbar-policy-alwayson.qml 1
665
666 Styles may use this property in combination with the \l active property
667 in order to implement transient scroll bars. Transient scroll bars are
668 hidden shortly after the last interaction event (hover or press). This
669 is typically done by animating the opacity of the scroll bar. To override
670 this behavior, set the policy to \c ScrollBar.AlwaysOn or
671 \c ScrollBar.AlwaysOff, depending on the size of the content compared to
672 its view. For example, for a vertical \l ListView:
673
674 \snippet qtquickcontrols-scrollbar-policy-alwayson-when-needed.qml 1
675*/
676QQuickScrollBar::Policy QQuickScrollBar::policy() const
677{
678 Q_D(const QQuickScrollBar);
679 return d->policy;
680}
681
682void QQuickScrollBar::setPolicy(Policy policy)
683{
684 Q_D(QQuickScrollBar);
685 if (d->policy == policy)
686 return;
687
688 d->policy = policy;
689 emit policyChanged();
690}
691
692/*!
693 \since QtQuick.Controls 2.3 (Qt 5.10)
694 \qmlproperty bool QtQuick.Controls::ScrollBar::horizontal
695 \readonly
696
697 This property holds whether the scroll bar is horizontal.
698
699 \sa orientation
700*/
701bool QQuickScrollBar::isHorizontal() const
702{
703 Q_D(const QQuickScrollBar);
704 return d->orientation == Qt::Horizontal;
705}
706
707/*!
708 \since QtQuick.Controls 2.3 (Qt 5.10)
709 \qmlproperty bool QtQuick.Controls::ScrollBar::vertical
710 \readonly
711
712 This property holds whether the scroll bar is vertical.
713
714 \sa orientation
715*/
716bool QQuickScrollBar::isVertical() const
717{
718 Q_D(const QQuickScrollBar);
719 return d->orientation == Qt::Vertical;
720}
721
722/*!
723 \since QtQuick.Controls 2.4 (Qt 5.11)
724 \qmlproperty real QtQuick.Controls::ScrollBar::minimumSize
725
726 This property holds the minimum size of the scroll bar, scaled to \c {0.0 - 1.0}.
727
728 \sa size, visualSize, visualPosition
729*/
730qreal QQuickScrollBar::minimumSize() const
731{
732 Q_D(const QQuickScrollBar);
733 return d->minimumSize;
734}
735
736void QQuickScrollBar::setMinimumSize(qreal minimumSize)
737{
738 Q_D(QQuickScrollBar);
739 if (!qt_is_finite(d: minimumSize) || qFuzzyCompare(p1: d->minimumSize, p2: minimumSize))
740 return;
741
742 auto oldVisualArea = d->visualArea();
743 d->minimumSize = qBound(min: 0.0, val: minimumSize, max: 1.0);
744 if (isComponentComplete())
745 d->resizeContent();
746 emit minimumSizeChanged();
747 d->visualAreaChange(newVisualArea: d->visualArea(), oldVisualArea);
748}
749
750/*!
751 \since QtQuick.Controls 2.4 (Qt 5.11)
752 \qmlproperty real QtQuick.Controls::ScrollBar::visualSize
753
754 This property holds the effective visual size of the scroll bar,
755 which may be limited by the \l {minimumSize}{minimum size}.
756
757 \sa size, minimumSize
758*/
759qreal QQuickScrollBar::visualSize() const
760{
761 Q_D(const QQuickScrollBar);
762 return d->visualArea().size;
763}
764
765/*!
766 \since QtQuick.Controls 2.4 (Qt 5.11)
767 \qmlproperty real QtQuick.Controls::ScrollBar::visualPosition
768
769 This property holds the effective visual position of the scroll bar,
770 which may be limited by the \l {minimumSize}{minimum size}.
771
772 \sa position, minimumSize
773*/
774qreal QQuickScrollBar::visualPosition() const
775{
776 Q_D(const QQuickScrollBar);
777 return d->visualArea().position;
778}
779
780QQuickIndicatorButton *QQuickScrollBar::decreaseVisual()
781{
782 Q_D(QQuickScrollBar);
783 return d->decreaseVisual;
784}
785
786QQuickIndicatorButton *QQuickScrollBar::increaseVisual()
787{
788 Q_D(QQuickScrollBar);
789 return d->increaseVisual;
790}
791
792/*!
793 \qmlmethod void QtQuick.Controls::ScrollBar::increase()
794
795 Increases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0.
796
797 \sa stepSize
798*/
799void QQuickScrollBar::increase()
800{
801 Q_D(QQuickScrollBar);
802 qreal step = qFuzzyIsNull(d: d->stepSize) ? 0.1 : d->stepSize;
803 bool wasActive = d->active;
804 setActive(true);
805 setPosition(qMin<qreal>(a: 1.0 - d->size, b: d->position + step));
806 setActive(wasActive);
807}
808
809/*!
810 \qmlmethod void QtQuick.Controls::ScrollBar::decrease()
811
812 Decreases the position by \l stepSize or \c 0.1 if stepSize is \c 0.0.
813
814 \sa stepSize
815*/
816void QQuickScrollBar::decrease()
817{
818 Q_D(QQuickScrollBar);
819 qreal step = qFuzzyIsNull(d: d->stepSize) ? 0.1 : d->stepSize;
820 bool wasActive = d->active;
821 setActive(true);
822 setPosition(qMax<qreal>(a: 0.0, b: d->position - step));
823 setActive(wasActive);
824}
825
826void QQuickScrollBar::mousePressEvent(QMouseEvent *event)
827{
828 Q_D(QQuickScrollBar);
829 QQuickControl::mousePressEvent(event);
830 d->handleMove(point: event->position(), timestamp: event->timestamp());
831}
832
833#if QT_CONFIG(quicktemplates2_hover)
834void QQuickScrollBar::hoverChange()
835{
836 Q_D(QQuickScrollBar);
837 d->updateActive();
838}
839
840void QQuickScrollBar::hoverEnterEvent(QHoverEvent *event)
841{
842 Q_D(QQuickScrollBar);
843 QQuickControl::hoverEnterEvent(event);
844 d->updateHover(pos: event->position());
845 event->ignore();
846}
847
848void QQuickScrollBar::hoverMoveEvent(QHoverEvent *event)
849{
850 Q_D(QQuickScrollBar);
851 QQuickControl::hoverMoveEvent(event);
852 d->updateHover(pos: event->position());
853 event->ignore();
854}
855
856void QQuickScrollBar::hoverLeaveEvent(QHoverEvent *event)
857{
858 Q_D(QQuickScrollBar);
859 QQuickControl::hoverLeaveEvent(event);
860
861 d->updateHover(pos: QPoint(), newHoverState: false); //position is not needed when we force it to unhover
862 event->ignore();
863}
864#endif
865
866void QQuickScrollBar::classBegin()
867{
868 Q_D(QQuickScrollBar);
869 QQuickControl::classBegin();
870
871 QQmlContext *context = qmlContext(this);
872 if (context) {
873 QQmlEngine::setContextForObject(d->decreaseVisual, context);
874 QQmlEngine::setContextForObject(d->increaseVisual, context);
875 }
876}
877
878void QQuickScrollBar::componentComplete()
879{
880 Q_D(QQuickScrollBar);
881 QQuickIndicatorButtonPrivate::get(button: d->decreaseVisual)->executeIndicator(complete: true);
882 QQuickIndicatorButtonPrivate::get(button: d->increaseVisual)->executeIndicator(complete: true);
883
884 QQuickControl::componentComplete();
885}
886
887#if QT_CONFIG(accessibility)
888void QQuickScrollBar::accessibilityActiveChanged(bool active)
889{
890 QQuickControl::accessibilityActiveChanged(active);
891
892 Q_D(QQuickScrollBar);
893 if (active) {
894 setAccessibleProperty(propertyName: "pressed", value: d->pressed);
895
896 if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(object: this)) {
897 connect(sender: accessibleAttached, signal: &QQuickAccessibleAttached::increaseAction, context: this, slot: &QQuickScrollBar::increase);
898 connect(sender: accessibleAttached, signal: &QQuickAccessibleAttached::decreaseAction, context: this, slot: &QQuickScrollBar::decrease);
899 }
900 } else {
901 if (QQuickAccessibleAttached *accessibleAttached = QQuickControlPrivate::accessibleAttached(object: this)) {
902 disconnect(sender: accessibleAttached, signal: &QQuickAccessibleAttached::increaseAction, receiver: this, slot: &QQuickScrollBar::increase);
903 disconnect(sender: accessibleAttached, signal: &QQuickAccessibleAttached::decreaseAction, receiver: this, slot: &QQuickScrollBar::decrease);
904 }
905 }
906}
907
908QAccessible::Role QQuickScrollBar::accessibleRole() const
909{
910 return QAccessible::ScrollBar;
911}
912#endif
913
914void QQuickScrollBarAttachedPrivate::setFlickable(QQuickFlickable *item)
915{
916 if (flickable) {
917 // NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size).
918 // The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling
919 // pointer on destruction.
920 QQuickItemPrivate::get(item: flickable)->removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
921 QQuickItemPrivate::get(item: flickable)->removeItemChangeListener(this, types: QQuickItemPrivate::Destroyed);
922 if (horizontal)
923 cleanupHorizontal();
924 if (vertical)
925 cleanupVertical();
926 }
927
928 flickable = item;
929
930 if (item) {
931 // Don't know how to combine these calls into one, and as long as they're separate calls,
932 // the remove* calls above need to be separate too, otherwise they will have no effect.
933 QQuickItemPrivate::get(item)->updateOrAddGeometryChangeListener(listener: this, types: QQuickGeometryChange::Size);
934 QQuickItemPrivate::get(item)->updateOrAddItemChangeListener(listener: this, types: QQuickItemPrivate::Destroyed);
935 if (horizontal)
936 initHorizontal();
937 if (vertical)
938 initVertical();
939 }
940}
941
942void QQuickScrollBarAttachedPrivate::initHorizontal()
943{
944 Q_ASSERT(flickable && horizontal);
945
946 connect(sender: flickable, signal: &QQuickFlickable::movingHorizontallyChanged, receiverPrivate: this, slot: &QQuickScrollBarAttachedPrivate::activateHorizontal);
947
948 // TODO: export QQuickFlickableVisibleArea
949 QObject *area = flickable->property(name: "visibleArea").value<QObject *>();
950 QObject::connect(sender: area, SIGNAL(widthRatioChanged(qreal)), receiver: horizontal, SLOT(setSize(qreal)));
951 QObject::connect(sender: area, SIGNAL(xPositionChanged(qreal)), receiver: horizontal, SLOT(setPosition(qreal)));
952
953 // ensure that the ScrollBar is stacked above the Flickable in a ScrollView
954 QQuickItem *parent = horizontal->parentItem();
955 if (parent && parent == flickable->parentItem())
956 horizontal->stackAfter(flickable);
957
958 // If a scroll bar was previously hidden (due to e.g. setting a new contentItem
959 // on a ScrollView), we need to make sure that we un-hide it.
960 if (auto control = qobject_cast<QQuickControl*>(object: q_func()->parent())) {
961 const auto visibility = horizontal->policy() != QQuickScrollBar::AlwaysOff
962 ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
963 QQuickControlPrivate::unhideOldItem(control, item: horizontal, visibility);
964 }
965
966 layoutHorizontal();
967 horizontal->setSize(area->property(name: "widthRatio").toReal());
968 horizontal->setPosition(area->property(name: "xPosition").toReal());
969}
970
971void QQuickScrollBarAttachedPrivate::initVertical()
972{
973 Q_ASSERT(flickable && vertical);
974
975 connect(sender: flickable, signal: &QQuickFlickable::movingVerticallyChanged, receiverPrivate: this, slot: &QQuickScrollBarAttachedPrivate::activateVertical);
976
977 // TODO: export QQuickFlickableVisibleArea
978 QObject *area = flickable->property(name: "visibleArea").value<QObject *>();
979 QObject::connect(sender: area, SIGNAL(heightRatioChanged(qreal)), receiver: vertical, SLOT(setSize(qreal)));
980 QObject::connect(sender: area, SIGNAL(yPositionChanged(qreal)), receiver: vertical, SLOT(setPosition(qreal)));
981
982 // ensure that the ScrollBar is stacked above the Flickable in a ScrollView
983 QQuickItem *parent = vertical->parentItem();
984 if (parent && parent == flickable->parentItem())
985 vertical->stackAfter(flickable);
986
987 if (auto control = qobject_cast<QQuickControl*>(object: q_func()->parent())) {
988 const auto visibility = vertical->policy() != QQuickScrollBar::AlwaysOff
989 ? QQuickControlPrivate::UnhideVisibility::Show : QQuickControlPrivate::UnhideVisibility::Hide;
990 QQuickControlPrivate::unhideOldItem(control, item: vertical, visibility);
991 }
992
993 layoutVertical();
994 vertical->setSize(area->property(name: "heightRatio").toReal());
995 vertical->setPosition(area->property(name: "yPosition").toReal());
996}
997
998void QQuickScrollBarAttachedPrivate::cleanupHorizontal()
999{
1000 Q_ASSERT(flickable && horizontal);
1001
1002 QQuickControlPrivate::hideOldItem(item: horizontal);
1003 // ScrollBar.qml has a binding to visible and ScrollView.qml has a binding to parent.
1004 // If we just set visible to false and parent to null, these bindings will overwrite
1005 // them upon component completion as part of the binding evaluation.
1006 // That's why we remove the binding completely.
1007 const QQmlProperty visibleProperty(horizontal, QStringLiteral("visible"));
1008 const QQmlProperty parentProperty(horizontal, QStringLiteral("parent"));
1009 QQmlPropertyPrivate::removeBinding(that: visibleProperty);
1010 QQmlPropertyPrivate::removeBinding(that: parentProperty);
1011
1012 disconnect(sender: flickable, signal: &QQuickFlickable::movingHorizontallyChanged, receiverPrivate: this, slot: &QQuickScrollBarAttachedPrivate::activateHorizontal);
1013
1014 // TODO: export QQuickFlickableVisibleArea
1015 QObject *area = flickable->property(name: "visibleArea").value<QObject *>();
1016 QObject::disconnect(sender: area, SIGNAL(widthRatioChanged(qreal)), receiver: horizontal, SLOT(setSize(qreal)));
1017 QObject::disconnect(sender: area, SIGNAL(xPositionChanged(qreal)), receiver: horizontal, SLOT(setPosition(qreal)));
1018}
1019
1020void QQuickScrollBarAttachedPrivate::cleanupVertical()
1021{
1022 Q_ASSERT(flickable && vertical);
1023
1024 QQuickControlPrivate::hideOldItem(item: vertical);
1025 const QQmlProperty visibleProperty(vertical, QStringLiteral("visible"));
1026 const QQmlProperty parentProperty(vertical, QStringLiteral("parent"));
1027 QQmlPropertyPrivate::removeBinding(that: visibleProperty);
1028 QQmlPropertyPrivate::removeBinding(that: parentProperty);
1029
1030 disconnect(sender: flickable, signal: &QQuickFlickable::movingVerticallyChanged, receiverPrivate: this, slot: &QQuickScrollBarAttachedPrivate::activateVertical);
1031
1032 // TODO: export QQuickFlickableVisibleArea
1033 QObject *area = flickable->property(name: "visibleArea").value<QObject *>();
1034 QObject::disconnect(sender: area, SIGNAL(heightRatioChanged(qreal)), receiver: vertical, SLOT(setSize(qreal)));
1035 QObject::disconnect(sender: area, SIGNAL(yPositionChanged(qreal)), receiver: vertical, SLOT(setPosition(qreal)));
1036}
1037
1038void QQuickScrollBarAttachedPrivate::activateHorizontal()
1039{
1040 QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(bar: horizontal);
1041 p->moving = flickable->isMovingHorizontally();
1042 p->updateActive();
1043}
1044
1045void QQuickScrollBarAttachedPrivate::activateVertical()
1046{
1047 QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(bar: vertical);
1048 p->moving = flickable->isMovingVertically();
1049 p->updateActive();
1050}
1051
1052// TODO: QQuickFlickable::maxXYExtent()
1053class QQuickFriendlyFlickable : public QQuickFlickable
1054{
1055 friend class QQuickScrollBarAttachedPrivate;
1056};
1057
1058void QQuickScrollBarAttachedPrivate::scrollHorizontal()
1059{
1060 if (!flickable)
1061 return;
1062
1063 QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable);
1064
1065 const qreal viewwidth = f->width();
1066 const qreal maxxextent = -f->maxXExtent() + f->minXExtent();
1067 const qreal cx = horizontal->position() * (maxxextent + viewwidth) - f->minXExtent();
1068
1069 if (!qIsNaN(d: cx) && !qFuzzyCompare(p1: cx, p2: flickable->contentX()))
1070 flickable->setContentX(cx);
1071}
1072
1073void QQuickScrollBarAttachedPrivate::scrollVertical()
1074{
1075 if (!flickable)
1076 return;
1077
1078 QQuickFriendlyFlickable *f = reinterpret_cast<QQuickFriendlyFlickable *>(flickable);
1079
1080 const qreal viewheight = f->height();
1081 const qreal maxyextent = -f->maxYExtent() + f->minYExtent();
1082 const qreal cy = vertical->position() * (maxyextent + viewheight) - f->minYExtent();
1083
1084 if (!qIsNaN(d: cy) && !qFuzzyCompare(p1: cy, p2: flickable->contentY()))
1085 flickable->setContentY(cy);
1086}
1087
1088void QQuickScrollBarAttachedPrivate::mirrorVertical()
1089{
1090 layoutVertical(move: true);
1091}
1092
1093void QQuickScrollBarAttachedPrivate::layoutHorizontal(bool move)
1094{
1095 Q_ASSERT(horizontal && flickable);
1096 if (horizontal->parentItem() != flickable)
1097 return;
1098 horizontal->setWidth(flickable->width());
1099 if (move)
1100 horizontal->setY(flickable->height() - horizontal->height());
1101}
1102
1103void QQuickScrollBarAttachedPrivate::layoutVertical(bool move)
1104{
1105 Q_ASSERT(vertical && flickable);
1106 if (vertical->parentItem() != flickable)
1107 return;
1108 vertical->setHeight(flickable->height());
1109 if (move)
1110 vertical->setX(vertical->isMirrored() ? 0 : flickable->width() - vertical->width());
1111}
1112
1113void QQuickScrollBarAttachedPrivate::itemGeometryChanged(QQuickItem *item, const QQuickGeometryChange change, const QRectF &diff)
1114{
1115 Q_UNUSED(item);
1116 Q_UNUSED(change);
1117 if (horizontal && horizontal->height() > 0) {
1118#ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
1119 bool move = qFuzzyIsNull(d: horizontal->y()) || qFuzzyCompare(p1: horizontal->y(), p2: diff.height() - horizontal->height());
1120#else
1121 bool move = qFuzzyIsNull(horizontal->y()) || qFuzzyCompare(horizontal->y(), item->height() - diff.height() - horizontal->height());
1122#endif
1123 if (flickable)
1124 layoutHorizontal(move);
1125 }
1126 if (vertical && vertical->width() > 0) {
1127#ifdef QT_QUICK_NEW_GEOMETRY_CHANGED_HANDLING // TODO: correct/rename diff to oldGeometry
1128 bool move = qFuzzyIsNull(d: vertical->x()) || qFuzzyCompare(p1: vertical->x(), p2: diff.width() - vertical->width());
1129#else
1130 bool move = qFuzzyIsNull(vertical->x()) || qFuzzyCompare(vertical->x(), item->width() - diff.width() - vertical->width());
1131#endif
1132 if (flickable)
1133 layoutVertical(move);
1134 }
1135}
1136
1137void QQuickScrollBarAttachedPrivate::itemImplicitWidthChanged(QQuickItem *item)
1138{
1139 if (item == vertical && flickable)
1140 layoutVertical(move: true);
1141}
1142
1143void QQuickScrollBarAttachedPrivate::itemImplicitHeightChanged(QQuickItem *item)
1144{
1145 if (item == horizontal && flickable)
1146 layoutHorizontal(move: true);
1147}
1148
1149void QQuickScrollBarAttachedPrivate::itemDestroyed(QQuickItem *item)
1150{
1151 if (item == flickable)
1152 flickable = nullptr;
1153 if (item == horizontal)
1154 horizontal = nullptr;
1155 if (item == vertical)
1156 vertical = nullptr;
1157}
1158
1159QQuickScrollBarAttached::QQuickScrollBarAttached(QObject *parent)
1160 : QObject(*(new QQuickScrollBarAttachedPrivate), parent)
1161{
1162 Q_D(QQuickScrollBarAttached);
1163 d->setFlickable(qobject_cast<QQuickFlickable *>(object: parent));
1164
1165 if (parent && !d->flickable && !qobject_cast<QQuickScrollView *>(object: parent))
1166 qmlWarning(me: parent) << "ScrollBar must be attached to a Flickable or ScrollView";
1167}
1168
1169QQuickScrollBarAttached::~QQuickScrollBarAttached()
1170{
1171 Q_D(QQuickScrollBarAttached);
1172 if (d->horizontal) {
1173 QQuickItemPrivate::get(item: d->horizontal)->removeItemChangeListener(d, types: QsbHorizontalChangeTypes);
1174 d->horizontal = nullptr;
1175 }
1176 if (d->vertical) {
1177 QQuickItemPrivate::get(item: d->vertical)->removeItemChangeListener(d, types: QsbVerticalChangeTypes);
1178 d->vertical = nullptr;
1179 }
1180 d->setFlickable(nullptr);
1181}
1182
1183/*!
1184 \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::horizontal
1185
1186 This property attaches a horizontal scroll bar to a \l Flickable.
1187
1188 \code
1189 Flickable {
1190 contentWidth: 2000
1191 ScrollBar.horizontal: ScrollBar { }
1192 }
1193 \endcode
1194
1195 \sa {Attaching ScrollBar to a Flickable}
1196*/
1197QQuickScrollBar *QQuickScrollBarAttached::horizontal() const
1198{
1199 Q_D(const QQuickScrollBarAttached);
1200 return d->horizontal;
1201}
1202
1203void QQuickScrollBarAttached::setHorizontal(QQuickScrollBar *horizontal)
1204{
1205 Q_D(QQuickScrollBarAttached);
1206 if (d->horizontal == horizontal)
1207 return;
1208
1209 if (d->horizontal) {
1210 QQuickItemPrivate::get(item: d->horizontal)->removeItemChangeListener(d, types: QsbHorizontalChangeTypes);
1211 QObjectPrivate::disconnect(sender: d->horizontal, signal: &QQuickScrollBar::positionChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::scrollHorizontal);
1212
1213 if (d->flickable)
1214 d->cleanupHorizontal();
1215 }
1216
1217 d->horizontal = horizontal;
1218
1219 if (horizontal) {
1220 if (!horizontal->parentItem())
1221 horizontal->setParentItem(qobject_cast<QQuickItem *>(o: parent()));
1222 horizontal->setOrientation(Qt::Horizontal);
1223
1224 QQuickItemPrivate::get(item: horizontal)->addItemChangeListener(listener: d, types: QsbHorizontalChangeTypes);
1225 QObjectPrivate::connect(sender: horizontal, signal: &QQuickScrollBar::positionChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::scrollHorizontal);
1226
1227 if (d->flickable)
1228 d->initHorizontal();
1229 }
1230 emit horizontalChanged();
1231}
1232
1233/*!
1234 \qmlattachedproperty ScrollBar QtQuick.Controls::ScrollBar::vertical
1235
1236 This property attaches a vertical scroll bar to a \l Flickable.
1237
1238 \code
1239 Flickable {
1240 contentHeight: 2000
1241 ScrollBar.vertical: ScrollBar { }
1242 }
1243 \endcode
1244
1245 \sa {Attaching ScrollBar to a Flickable}
1246*/
1247QQuickScrollBar *QQuickScrollBarAttached::vertical() const
1248{
1249 Q_D(const QQuickScrollBarAttached);
1250 return d->vertical;
1251}
1252
1253void QQuickScrollBarAttached::setVertical(QQuickScrollBar *vertical)
1254{
1255 Q_D(QQuickScrollBarAttached);
1256 if (d->vertical == vertical)
1257 return;
1258
1259 if (d->vertical) {
1260 QQuickItemPrivate::get(item: d->vertical)->removeItemChangeListener(d, types: QsbVerticalChangeTypes);
1261 QObjectPrivate::disconnect(sender: d->vertical, signal: &QQuickScrollBar::mirroredChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::mirrorVertical);
1262 QObjectPrivate::disconnect(sender: d->vertical, signal: &QQuickScrollBar::positionChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::scrollVertical);
1263
1264 if (d->flickable)
1265 d->cleanupVertical();
1266 }
1267
1268 d->vertical = vertical;
1269
1270 if (vertical) {
1271 if (!vertical->parentItem())
1272 vertical->setParentItem(qobject_cast<QQuickItem *>(o: parent()));
1273 vertical->setOrientation(Qt::Vertical);
1274
1275 QQuickItemPrivate::get(item: vertical)->addItemChangeListener(listener: d, types: QsbVerticalChangeTypes);
1276 QObjectPrivate::connect(sender: vertical, signal: &QQuickScrollBar::mirroredChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::mirrorVertical);
1277 QObjectPrivate::connect(sender: vertical, signal: &QQuickScrollBar::positionChanged, receiverPrivate: d, slot: &QQuickScrollBarAttachedPrivate::scrollVertical);
1278
1279 if (d->flickable)
1280 d->initVertical();
1281 }
1282 emit verticalChanged();
1283}
1284
1285QT_END_NAMESPACE
1286
1287#include "moc_qquickscrollbar_p.cpp"
1288

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