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

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