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