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