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 "qquickslider_p.h" |
5 | #include "qquickcontrol_p_p.h" |
6 | #include "qquickdeferredexecute_p_p.h" |
7 | |
8 | #include <QtQuick/private/qquickwindow_p.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | /*! |
13 | \qmltype Slider |
14 | \inherits Control |
15 | //! \instantiates QQuickSlider |
16 | \inqmlmodule QtQuick.Controls |
17 | \since 5.7 |
18 | \ingroup qtquickcontrols-input |
19 | \brief Used to select a value by sliding a handle along a track. |
20 | |
21 | \image qtquickcontrols-slider.gif |
22 | |
23 | Slider is used to select a value by sliding a handle along a track. |
24 | |
25 | In the example below, custom \l from, \l value, and \l to values are set: |
26 | |
27 | \code |
28 | Slider { |
29 | from: 1 |
30 | value: 25 |
31 | to: 100 |
32 | } |
33 | \endcode |
34 | |
35 | The \l position property is expressed as a fraction of the control's size, |
36 | in the range \c {0.0 - 1.0}. The \l visualPosition property is |
37 | the same, except that it is reversed in a |
38 | \l {Right-to-left User Interfaces}{right-to-left} application. The |
39 | visualPosition is useful for positioning the handle when styling Slider. |
40 | In the example above, \l visualPosition will be \c 0.24 in a left-to-right |
41 | application, and \c 0.76 in a right-to-left application. |
42 | |
43 | For a slider that allows the user to select a range by providing two |
44 | handles, see \l RangeSlider. |
45 | |
46 | \sa {Customizing Slider}, {Input Controls} |
47 | */ |
48 | |
49 | /*! |
50 | \since QtQuick.Controls 2.2 (Qt 5.9) |
51 | \qmlsignal QtQuick.Controls::Slider::moved() |
52 | |
53 | This signal is emitted when the slider has been interactively moved |
54 | by the user by either touch, mouse, wheel, or keys. |
55 | */ |
56 | |
57 | class QQuickSliderPrivate : public QQuickControlPrivate |
58 | { |
59 | Q_DECLARE_PUBLIC(QQuickSlider) |
60 | |
61 | public: |
62 | qreal snapPosition(qreal position) const; |
63 | qreal positionAt(const QPointF &point) const; |
64 | void setPosition(qreal position); |
65 | void updatePosition(); |
66 | |
67 | bool handlePress(const QPointF &point, ulong timestamp) override; |
68 | bool handleMove(const QPointF &point, ulong timestamp) override; |
69 | bool handleRelease(const QPointF &point, ulong timestamp) override; |
70 | void handleUngrab() override; |
71 | |
72 | void cancelHandle(); |
73 | void executeHandle(bool complete = false); |
74 | |
75 | void itemImplicitWidthChanged(QQuickItem *item) override; |
76 | void itemImplicitHeightChanged(QQuickItem *item) override; |
77 | |
78 | qreal from = 0; |
79 | qreal to = 1; |
80 | qreal value = 0; |
81 | qreal position = 0; |
82 | qreal stepSize = 0; |
83 | qreal touchDragThreshold = -1; // in QQuickWindowPrivate::dragOverThreshold, '-1' implies using styleHints::startDragDistance() |
84 | bool live = true; |
85 | bool pressed = false; |
86 | QPointF pressPoint; |
87 | Qt::Orientation orientation = Qt::Horizontal; |
88 | QQuickSlider::SnapMode snapMode = QQuickSlider::NoSnap; |
89 | QQuickDeferredPointer<QQuickItem> handle; |
90 | }; |
91 | |
92 | qreal QQuickSliderPrivate::snapPosition(qreal position) const |
93 | { |
94 | const qreal range = to - from; |
95 | if (qFuzzyIsNull(d: range)) |
96 | return position; |
97 | |
98 | const qreal effectiveStep = stepSize / range; |
99 | if (qFuzzyIsNull(d: effectiveStep)) |
100 | return position; |
101 | |
102 | return qRound(d: position / effectiveStep) * effectiveStep; |
103 | } |
104 | |
105 | qreal QQuickSliderPrivate::positionAt(const QPointF &point) const |
106 | { |
107 | Q_Q(const QQuickSlider); |
108 | qreal pos = 0.0; |
109 | if (orientation == Qt::Horizontal) { |
110 | const qreal hw = handle ? handle->width() : 0; |
111 | const qreal offset = hw / 2; |
112 | const qreal extent = q->availableWidth() - hw; |
113 | if (!qFuzzyIsNull(d: extent)) { |
114 | if (q->isMirrored()) |
115 | pos = (q->width() - point.x() - q->rightPadding() - offset) / extent; |
116 | else |
117 | pos = (point.x() - q->leftPadding() - offset) / extent; |
118 | } |
119 | } else { |
120 | const qreal hh = handle ? handle->height() : 0; |
121 | const qreal offset = hh / 2; |
122 | const qreal extent = q->availableHeight() - hh; |
123 | if (!qFuzzyIsNull(d: extent)) |
124 | pos = (q->height() - point.y() - q->bottomPadding() - offset) / extent; |
125 | } |
126 | return qBound<qreal>(min: 0.0, val: pos, max: 1.0); |
127 | } |
128 | |
129 | void QQuickSliderPrivate::setPosition(qreal pos) |
130 | { |
131 | Q_Q(QQuickSlider); |
132 | pos = qBound<qreal>(min: 0.0, val: pos, max: 1.0); |
133 | if (qFuzzyCompare(p1: position, p2: pos)) |
134 | return; |
135 | |
136 | position = pos; |
137 | emit q->positionChanged(); |
138 | emit q->visualPositionChanged(); |
139 | } |
140 | |
141 | void QQuickSliderPrivate::updatePosition() |
142 | { |
143 | qreal pos = 0; |
144 | if (!qFuzzyCompare(p1: from, p2: to)) |
145 | pos = (value - from) / (to - from); |
146 | setPosition(pos); |
147 | } |
148 | |
149 | bool QQuickSliderPrivate::handlePress(const QPointF &point, ulong timestamp) |
150 | { |
151 | Q_Q(QQuickSlider); |
152 | QQuickControlPrivate::handlePress(point, timestamp); |
153 | pressPoint = point; |
154 | q->setPressed(true); |
155 | return true; |
156 | } |
157 | |
158 | bool QQuickSliderPrivate::handleMove(const QPointF &point, ulong timestamp) |
159 | { |
160 | Q_Q(QQuickSlider); |
161 | QQuickControlPrivate::handleMove(point, timestamp); |
162 | const qreal oldPos = position; |
163 | qreal pos = positionAt(point); |
164 | if (snapMode == QQuickSlider::SnapAlways) |
165 | pos = snapPosition(position: pos); |
166 | if (live) |
167 | q->setValue(q->valueAt(position: pos)); |
168 | if (!live || snapMode == QQuickSlider::NoSnap || snapMode == QQuickSlider::SnapOnRelease) |
169 | setPosition(pos); |
170 | if (!qFuzzyCompare(p1: pos, p2: oldPos)) |
171 | emit q->moved(); |
172 | return true; |
173 | } |
174 | |
175 | bool QQuickSliderPrivate::handleRelease(const QPointF &point, ulong timestamp) |
176 | { |
177 | Q_Q(QQuickSlider); |
178 | QQuickControlPrivate::handleRelease(point, timestamp); |
179 | pressPoint = QPointF(); |
180 | const qreal oldPos = position; |
181 | qreal pos = positionAt(point); |
182 | if (snapMode != QQuickSlider::NoSnap) |
183 | pos = snapPosition(position: pos); |
184 | qreal val = q->valueAt(position: pos); |
185 | if (!qFuzzyCompare(p1: val, p2: value)) |
186 | q->setValue(val); |
187 | else if (snapMode != QQuickSlider::NoSnap) |
188 | setPosition(pos); |
189 | if (!qFuzzyCompare(p1: pos, p2: oldPos)) |
190 | emit q->moved(); |
191 | q->setKeepMouseGrab(false); |
192 | q->setKeepTouchGrab(false); |
193 | q->setPressed(false); |
194 | return true; |
195 | } |
196 | |
197 | void QQuickSliderPrivate::handleUngrab() |
198 | { |
199 | Q_Q(QQuickSlider); |
200 | QQuickControlPrivate::handleUngrab(); |
201 | pressPoint = QPointF(); |
202 | q->setPressed(false); |
203 | } |
204 | |
205 | void QQuickSliderPrivate::cancelHandle() |
206 | { |
207 | Q_Q(QQuickSlider); |
208 | quickCancelDeferred(object: q, property: handleName()); |
209 | } |
210 | |
211 | void QQuickSliderPrivate::executeHandle(bool complete) |
212 | { |
213 | Q_Q(QQuickSlider); |
214 | if (handle.wasExecuted()) |
215 | return; |
216 | |
217 | if (!handle || complete) |
218 | quickBeginDeferred(object: q, property: handleName(), delegate&: handle); |
219 | if (complete) |
220 | quickCompleteDeferred(object: q, property: handleName(), delegate&: handle); |
221 | } |
222 | |
223 | void QQuickSliderPrivate::itemImplicitWidthChanged(QQuickItem *item) |
224 | { |
225 | Q_Q(QQuickSlider); |
226 | QQuickControlPrivate::itemImplicitWidthChanged(item); |
227 | if (item == handle) |
228 | emit q->implicitHandleWidthChanged(); |
229 | } |
230 | |
231 | void QQuickSliderPrivate::itemImplicitHeightChanged(QQuickItem *item) |
232 | { |
233 | Q_Q(QQuickSlider); |
234 | QQuickControlPrivate::itemImplicitHeightChanged(item); |
235 | if (item == handle) |
236 | emit q->implicitHandleHeightChanged(); |
237 | } |
238 | |
239 | QQuickSlider::QQuickSlider(QQuickItem *parent) |
240 | : QQuickControl(*(new QQuickSliderPrivate), parent) |
241 | { |
242 | setActiveFocusOnTab(true); |
243 | #ifdef Q_OS_MACOS |
244 | setFocusPolicy(Qt::TabFocus); |
245 | #else |
246 | setFocusPolicy(Qt::StrongFocus); |
247 | #endif |
248 | setAcceptedMouseButtons(Qt::LeftButton); |
249 | #if QT_CONFIG(quicktemplates2_multitouch) |
250 | setAcceptTouchEvents(true); |
251 | #endif |
252 | #if QT_CONFIG(cursor) |
253 | setCursor(Qt::ArrowCursor); |
254 | #endif |
255 | } |
256 | |
257 | QQuickSlider::~QQuickSlider() |
258 | { |
259 | Q_D(QQuickSlider); |
260 | d->removeImplicitSizeListener(item: d->handle); |
261 | } |
262 | |
263 | /*! |
264 | \qmlproperty real QtQuick.Controls::Slider::from |
265 | |
266 | This property holds the starting value for the range. The default value is \c 0.0. |
267 | |
268 | \sa to, value |
269 | */ |
270 | qreal QQuickSlider::from() const |
271 | { |
272 | Q_D(const QQuickSlider); |
273 | return d->from; |
274 | } |
275 | |
276 | void QQuickSlider::setFrom(qreal from) |
277 | { |
278 | Q_D(QQuickSlider); |
279 | if (qFuzzyCompare(p1: d->from, p2: from)) |
280 | return; |
281 | |
282 | d->from = from; |
283 | emit fromChanged(); |
284 | if (isComponentComplete()) { |
285 | setValue(d->value); |
286 | d->updatePosition(); |
287 | } |
288 | } |
289 | |
290 | /*! |
291 | \qmlproperty real QtQuick.Controls::Slider::to |
292 | |
293 | This property holds the end value for the range. The default value is \c 1.0. |
294 | |
295 | \sa from, value |
296 | */ |
297 | qreal QQuickSlider::to() const |
298 | { |
299 | Q_D(const QQuickSlider); |
300 | return d->to; |
301 | } |
302 | |
303 | void QQuickSlider::setTo(qreal to) |
304 | { |
305 | Q_D(QQuickSlider); |
306 | if (qFuzzyCompare(p1: d->to, p2: to)) |
307 | return; |
308 | |
309 | d->to = to; |
310 | emit toChanged(); |
311 | if (isComponentComplete()) { |
312 | setValue(d->value); |
313 | d->updatePosition(); |
314 | } |
315 | } |
316 | |
317 | /*! |
318 | \qmlproperty real QtQuick.Controls::Slider::value |
319 | |
320 | This property holds the value in the range \c from - \c to. The default value is \c 0.0. |
321 | |
322 | \sa position |
323 | */ |
324 | qreal QQuickSlider::value() const |
325 | { |
326 | Q_D(const QQuickSlider); |
327 | return d->value; |
328 | } |
329 | |
330 | void QQuickSlider::setValue(qreal value) |
331 | { |
332 | Q_D(QQuickSlider); |
333 | if (isComponentComplete()) |
334 | value = d->from > d->to ? qBound(min: d->to, val: value, max: d->from) : qBound(min: d->from, val: value, max: d->to); |
335 | |
336 | if (qFuzzyCompare(p1: d->value, p2: value)) |
337 | return; |
338 | |
339 | d->value = value; |
340 | d->updatePosition(); |
341 | emit valueChanged(); |
342 | } |
343 | |
344 | /*! |
345 | \qmlproperty real QtQuick.Controls::Slider::position |
346 | \readonly |
347 | |
348 | This property holds the logical position of the handle. |
349 | |
350 | The position is expressed as a fraction of the control's size, in the range |
351 | \c {0.0 - 1.0}. For visualizing a slider, the right-to-left aware |
352 | \l visualPosition should be used instead. |
353 | |
354 | \sa value, visualPosition, valueAt() |
355 | */ |
356 | qreal QQuickSlider::position() const |
357 | { |
358 | Q_D(const QQuickSlider); |
359 | return d->position; |
360 | } |
361 | |
362 | /*! |
363 | \qmlproperty real QtQuick.Controls::Slider::visualPosition |
364 | \readonly |
365 | |
366 | This property holds the visual position of the handle. |
367 | |
368 | The position is expressed as a fraction of the control's size, in the range |
369 | \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the |
370 | value is equal to \c {1.0 - position}. This makes the value suitable for |
371 | visualizing the slider, taking right-to-left support into account. |
372 | |
373 | \sa position |
374 | */ |
375 | qreal QQuickSlider::visualPosition() const |
376 | { |
377 | Q_D(const QQuickSlider); |
378 | if (d->orientation == Qt::Vertical || isMirrored()) |
379 | return 1.0 - d->position; |
380 | return d->position; |
381 | } |
382 | |
383 | /*! |
384 | \qmlproperty real QtQuick.Controls::Slider::stepSize |
385 | |
386 | This property holds the step size. The default value is \c 0.0. |
387 | |
388 | \sa snapMode, increase(), decrease() |
389 | */ |
390 | qreal QQuickSlider::stepSize() const |
391 | { |
392 | Q_D(const QQuickSlider); |
393 | return d->stepSize; |
394 | } |
395 | |
396 | void QQuickSlider::setStepSize(qreal step) |
397 | { |
398 | Q_D(QQuickSlider); |
399 | if (qFuzzyCompare(p1: d->stepSize, p2: step)) |
400 | return; |
401 | |
402 | d->stepSize = step; |
403 | emit stepSizeChanged(); |
404 | } |
405 | |
406 | /*! |
407 | \qmlproperty enumeration QtQuick.Controls::Slider::snapMode |
408 | |
409 | This property holds the snap mode. |
410 | |
411 | The snap mode determines how the slider handle behaves with |
412 | regards to the \l stepSize. |
413 | |
414 | Possible values: |
415 | \value Slider.NoSnap The slider does not snap (default). |
416 | \value Slider.SnapAlways The slider snaps while the handle is dragged. |
417 | \value Slider.SnapOnRelease The slider does not snap while being dragged, but only after the handle is released. |
418 | |
419 | In the following table, the various modes are illustrated with animations. |
420 | The movement of the mouse cursor and the \l stepSize (\c 0.2) are identical |
421 | in each animation. |
422 | |
423 | \table |
424 | \header |
425 | \row \li \b Value \li \b Example |
426 | \row \li \c Slider.NoSnap \li \image qtquickcontrols-slider-nosnap.gif |
427 | \row \li \c Slider.SnapAlways \li \image qtquickcontrols-slider-snapalways.gif |
428 | \row \li \c Slider.SnapOnRelease \li \image qtquickcontrols-slider-snaponrelease.gif |
429 | \endtable |
430 | |
431 | \sa stepSize |
432 | */ |
433 | QQuickSlider::SnapMode QQuickSlider::snapMode() const |
434 | { |
435 | Q_D(const QQuickSlider); |
436 | return d->snapMode; |
437 | } |
438 | |
439 | void QQuickSlider::setSnapMode(SnapMode mode) |
440 | { |
441 | Q_D(QQuickSlider); |
442 | if (d->snapMode == mode) |
443 | return; |
444 | |
445 | d->snapMode = mode; |
446 | emit snapModeChanged(); |
447 | } |
448 | |
449 | /*! |
450 | \qmlproperty bool QtQuick.Controls::Slider::pressed |
451 | |
452 | This property holds whether the slider is pressed by either touch, mouse, |
453 | or keys. |
454 | */ |
455 | bool QQuickSlider::isPressed() const |
456 | { |
457 | Q_D(const QQuickSlider); |
458 | return d->pressed; |
459 | } |
460 | |
461 | void QQuickSlider::setPressed(bool pressed) |
462 | { |
463 | Q_D(QQuickSlider); |
464 | if (d->pressed == pressed) |
465 | return; |
466 | |
467 | d->pressed = pressed; |
468 | setAccessibleProperty(propertyName: "pressed" , value: pressed); |
469 | emit pressedChanged(); |
470 | } |
471 | |
472 | /*! |
473 | \since QtQuick.Controls 2.3 (Qt 5.10) |
474 | \qmlproperty bool QtQuick.Controls::Slider::horizontal |
475 | \readonly |
476 | |
477 | This property holds whether the slider is horizontal. |
478 | |
479 | \sa orientation |
480 | */ |
481 | bool QQuickSlider::isHorizontal() const |
482 | { |
483 | Q_D(const QQuickSlider); |
484 | return d->orientation == Qt::Horizontal; |
485 | } |
486 | |
487 | /*! |
488 | \since QtQuick.Controls 2.3 (Qt 5.10) |
489 | \qmlproperty bool QtQuick.Controls::Slider::vertical |
490 | \readonly |
491 | |
492 | This property holds whether the slider is vertical. |
493 | |
494 | \sa orientation |
495 | */ |
496 | bool QQuickSlider::isVertical() const |
497 | { |
498 | Q_D(const QQuickSlider); |
499 | return d->orientation == Qt::Vertical; |
500 | } |
501 | |
502 | /*! |
503 | \qmlproperty enumeration QtQuick.Controls::Slider::orientation |
504 | |
505 | This property holds the orientation. |
506 | |
507 | Possible values: |
508 | \value Qt.Horizontal Horizontal (default) |
509 | \value Qt.Vertical Vertical |
510 | |
511 | \sa horizontal, vertical |
512 | */ |
513 | Qt::Orientation QQuickSlider::orientation() const |
514 | { |
515 | Q_D(const QQuickSlider); |
516 | return d->orientation; |
517 | } |
518 | |
519 | void QQuickSlider::setOrientation(Qt::Orientation orientation) |
520 | { |
521 | Q_D(QQuickSlider); |
522 | if (d->orientation == orientation) |
523 | return; |
524 | |
525 | d->orientation = orientation; |
526 | emit orientationChanged(); |
527 | } |
528 | |
529 | /*! |
530 | \qmlproperty Item QtQuick.Controls::Slider::handle |
531 | |
532 | This property holds the handle item. |
533 | |
534 | \sa {Customizing Slider} |
535 | */ |
536 | QQuickItem *QQuickSlider::handle() const |
537 | { |
538 | QQuickSliderPrivate *d = const_cast<QQuickSliderPrivate *>(d_func()); |
539 | if (!d->handle) |
540 | d->executeHandle(); |
541 | return d->handle; |
542 | } |
543 | |
544 | void QQuickSlider::setHandle(QQuickItem *handle) |
545 | { |
546 | Q_D(QQuickSlider); |
547 | if (d->handle == handle) |
548 | return; |
549 | |
550 | QQuickControlPrivate::warnIfCustomizationNotSupported(control: this, item: handle, QStringLiteral("handle" )); |
551 | |
552 | if (!d->handle.isExecuting()) |
553 | d->cancelHandle(); |
554 | |
555 | const qreal oldImplicitHandleWidth = implicitHandleWidth(); |
556 | const qreal oldImplicitHandleHeight = implicitHandleHeight(); |
557 | |
558 | d->removeImplicitSizeListener(item: d->handle); |
559 | QQuickControlPrivate::hideOldItem(item: d->handle); |
560 | d->handle = handle; |
561 | |
562 | if (handle) { |
563 | if (!handle->parentItem()) |
564 | handle->setParentItem(this); |
565 | d->addImplicitSizeListener(item: handle); |
566 | } |
567 | |
568 | if (!qFuzzyCompare(p1: oldImplicitHandleWidth, p2: implicitHandleWidth())) |
569 | emit implicitHandleWidthChanged(); |
570 | if (!qFuzzyCompare(p1: oldImplicitHandleHeight, p2: implicitHandleHeight())) |
571 | emit implicitHandleHeightChanged(); |
572 | if (!d->handle.isExecuting()) |
573 | emit handleChanged(); |
574 | } |
575 | |
576 | /*! |
577 | \since QtQuick.Controls 2.1 (Qt 5.8) |
578 | \qmlmethod real QtQuick.Controls::Slider::valueAt(real position) |
579 | |
580 | Returns the value for the given \a position. |
581 | |
582 | \sa value, position |
583 | */ |
584 | qreal QQuickSlider::valueAt(qreal position) const |
585 | { |
586 | Q_D(const QQuickSlider); |
587 | const qreal value = (d->to - d->from) * position; |
588 | if (qFuzzyIsNull(d: d->stepSize)) |
589 | return d->from + value; |
590 | return d->from + qRound(d: value / d->stepSize) * d->stepSize; |
591 | } |
592 | |
593 | /*! |
594 | \since QtQuick.Controls 2.2 (Qt 5.9) |
595 | \qmlproperty bool QtQuick.Controls::Slider::live |
596 | |
597 | This property holds whether the slider provides live updates for the \l value |
598 | property while the handle is dragged. |
599 | |
600 | The default value is \c true. |
601 | |
602 | \sa value, valueAt() |
603 | */ |
604 | bool QQuickSlider::live() const |
605 | { |
606 | Q_D(const QQuickSlider); |
607 | return d->live; |
608 | } |
609 | |
610 | void QQuickSlider::setLive(bool live) |
611 | { |
612 | Q_D(QQuickSlider); |
613 | if (d->live == live) |
614 | return; |
615 | |
616 | d->live = live; |
617 | emit liveChanged(); |
618 | } |
619 | |
620 | /*! |
621 | \qmlmethod void QtQuick.Controls::Slider::increase() |
622 | |
623 | Increases the value by \l stepSize or \c 0.1 if stepSize is not defined. |
624 | |
625 | \sa stepSize |
626 | */ |
627 | void QQuickSlider::increase() |
628 | { |
629 | Q_D(QQuickSlider); |
630 | qreal step = qFuzzyIsNull(d: d->stepSize) ? 0.1 : d->stepSize; |
631 | setValue(d->value + step); |
632 | } |
633 | |
634 | /*! |
635 | \qmlmethod void QtQuick.Controls::Slider::decrease() |
636 | |
637 | Decreases the value by \l stepSize or \c 0.1 if stepSize is not defined. |
638 | |
639 | \sa stepSize |
640 | */ |
641 | void QQuickSlider::decrease() |
642 | { |
643 | Q_D(QQuickSlider); |
644 | qreal step = qFuzzyIsNull(d: d->stepSize) ? 0.1 : d->stepSize; |
645 | setValue(d->value - step); |
646 | } |
647 | |
648 | /*! |
649 | \since QtQuick.Controls 2.5 (Qt 5.12) |
650 | \qmlproperty qreal QtQuick.Controls::Slider::touchDragThreshold |
651 | |
652 | This property holds the threshold (in logical pixels) at which a touch drag event will be initiated. |
653 | The mouse drag threshold won't be affected. |
654 | The default value is \c Qt.styleHints.startDragDistance. |
655 | |
656 | \sa QStyleHints |
657 | */ |
658 | qreal QQuickSlider::touchDragThreshold() const |
659 | { |
660 | Q_D(const QQuickSlider); |
661 | return d->touchDragThreshold; |
662 | } |
663 | |
664 | void QQuickSlider::setTouchDragThreshold(qreal touchDragThreshold) |
665 | { |
666 | Q_D(QQuickSlider); |
667 | if (d->touchDragThreshold == touchDragThreshold) |
668 | return; |
669 | |
670 | d->touchDragThreshold = touchDragThreshold; |
671 | emit touchDragThresholdChanged(); |
672 | } |
673 | |
674 | void QQuickSlider::resetTouchDragThreshold() |
675 | { |
676 | setTouchDragThreshold(-1); |
677 | } |
678 | |
679 | /*! |
680 | \since QtQuick.Controls 2.5 (Qt 5.12) |
681 | \qmlproperty real QtQuick.Controls::Slider::implicitHandleWidth |
682 | \readonly |
683 | |
684 | This property holds the implicit handle width. |
685 | |
686 | The value is equal to \c {handle ? handle.implicitWidth : 0}. |
687 | |
688 | This is typically used, together with \l {Control::}{implicitContentWidth} and |
689 | \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}. |
690 | |
691 | \sa implicitHandleHeight |
692 | */ |
693 | qreal QQuickSlider::implicitHandleWidth() const |
694 | { |
695 | Q_D(const QQuickSlider); |
696 | if (!d->handle) |
697 | return 0; |
698 | return d->handle->implicitWidth(); |
699 | } |
700 | |
701 | /*! |
702 | \since QtQuick.Controls 2.5 (Qt 5.12) |
703 | \qmlproperty real QtQuick.Controls::Slider::implicitHandleHeight |
704 | \readonly |
705 | |
706 | This property holds the implicit handle height. |
707 | |
708 | The value is equal to \c {handle ? handle.implicitHeight : 0}. |
709 | |
710 | This is typically used, together with \l {Control::}{implicitContentHeight} and |
711 | \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}. |
712 | |
713 | \sa implicitHandleWidth |
714 | */ |
715 | qreal QQuickSlider::implicitHandleHeight() const |
716 | { |
717 | Q_D(const QQuickSlider); |
718 | if (!d->handle) |
719 | return 0; |
720 | return d->handle->implicitHeight(); |
721 | } |
722 | |
723 | void QQuickSlider::keyPressEvent(QKeyEvent *event) |
724 | { |
725 | Q_D(QQuickSlider); |
726 | QQuickControl::keyPressEvent(event); |
727 | |
728 | const qreal oldValue = d->value; |
729 | if (d->orientation == Qt::Horizontal) { |
730 | if (event->key() == Qt::Key_Left) { |
731 | setPressed(true); |
732 | if (isMirrored()) |
733 | increase(); |
734 | else |
735 | decrease(); |
736 | event->accept(); |
737 | } else if (event->key() == Qt::Key_Right) { |
738 | setPressed(true); |
739 | if (isMirrored()) |
740 | decrease(); |
741 | else |
742 | increase(); |
743 | event->accept(); |
744 | } |
745 | } else { |
746 | if (event->key() == Qt::Key_Up) { |
747 | setPressed(true); |
748 | increase(); |
749 | event->accept(); |
750 | } else if (event->key() == Qt::Key_Down) { |
751 | setPressed(true); |
752 | decrease(); |
753 | event->accept(); |
754 | } |
755 | } |
756 | if (!qFuzzyCompare(p1: d->value, p2: oldValue)) |
757 | emit moved(); |
758 | } |
759 | |
760 | void QQuickSlider::keyReleaseEvent(QKeyEvent *event) |
761 | { |
762 | QQuickControl::keyReleaseEvent(event); |
763 | setPressed(false); |
764 | } |
765 | |
766 | void QQuickSlider::mousePressEvent(QMouseEvent *event) |
767 | { |
768 | Q_D(QQuickSlider); |
769 | QQuickControl::mousePressEvent(event); |
770 | d->handleMove(point: event->position(), timestamp: event->timestamp()); |
771 | setKeepMouseGrab(true); |
772 | } |
773 | |
774 | #if QT_CONFIG(quicktemplates2_multitouch) |
775 | void QQuickSlider::touchEvent(QTouchEvent *event) |
776 | { |
777 | Q_D(QQuickSlider); |
778 | switch (event->type()) { |
779 | case QEvent::TouchUpdate: |
780 | for (const QTouchEvent::TouchPoint &point : event->points()) { |
781 | if (!d->acceptTouch(point)) |
782 | continue; |
783 | |
784 | switch (point.state()) { |
785 | case QEventPoint::Pressed: |
786 | d->handlePress(point: point.position(), timestamp: event->timestamp()); |
787 | break; |
788 | case QEventPoint::Updated: |
789 | if (!keepTouchGrab()) { |
790 | if (d->orientation == Qt::Horizontal) |
791 | setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().x() - d->pressPoint.x(), axis: Qt::XAxis, tp: &point, startDragThreshold: qRound(d: d->touchDragThreshold))); |
792 | else |
793 | setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().y() - d->pressPoint.y(), axis: Qt::YAxis, tp: &point, startDragThreshold: qRound(d: d->touchDragThreshold))); |
794 | } |
795 | if (keepTouchGrab()) |
796 | d->handleMove(point: point.position(), timestamp: event->timestamp()); |
797 | break; |
798 | case QEventPoint::Released: |
799 | d->handleRelease(point: point.position(), timestamp: event->timestamp()); |
800 | break; |
801 | default: |
802 | break; |
803 | } |
804 | } |
805 | break; |
806 | |
807 | default: |
808 | QQuickControl::touchEvent(event); |
809 | break; |
810 | } |
811 | } |
812 | #endif |
813 | |
814 | #if QT_CONFIG(wheelevent) |
815 | void QQuickSlider::wheelEvent(QWheelEvent *event) |
816 | { |
817 | Q_D(QQuickSlider); |
818 | QQuickControl::wheelEvent(event); |
819 | if (d->wheelEnabled) { |
820 | const qreal oldValue = d->value; |
821 | const QPointF angle = event->angleDelta(); |
822 | const qreal delta = (qFuzzyIsNull(d: angle.y()) ? angle.x() : (event->inverted() ? -angle.y() : angle.y())) / int(QWheelEvent::DefaultDeltasPerStep); |
823 | const qreal step = qFuzzyIsNull(d: d->stepSize) ? 0.1 : d->stepSize; |
824 | setValue(oldValue + step * delta); |
825 | const bool wasMoved = !qFuzzyCompare(p1: d->value, p2: oldValue); |
826 | if (wasMoved) |
827 | emit moved(); |
828 | } |
829 | } |
830 | #endif |
831 | |
832 | void QQuickSlider::mirrorChange() |
833 | { |
834 | QQuickControl::mirrorChange(); |
835 | emit visualPositionChanged(); |
836 | } |
837 | |
838 | void QQuickSlider::componentComplete() |
839 | { |
840 | Q_D(QQuickSlider); |
841 | d->executeHandle(complete: true); |
842 | QQuickControl::componentComplete(); |
843 | setValue(d->value); |
844 | d->updatePosition(); |
845 | } |
846 | |
847 | #if QT_CONFIG(accessibility) |
848 | void QQuickSlider::accessibilityActiveChanged(bool active) |
849 | { |
850 | QQuickControl::accessibilityActiveChanged(active); |
851 | |
852 | Q_D(QQuickSlider); |
853 | if (active) |
854 | setAccessibleProperty(propertyName: "pressed" , value: d->pressed); |
855 | } |
856 | |
857 | QAccessible::Role QQuickSlider::accessibleRole() const |
858 | { |
859 | return QAccessible::Slider; |
860 | } |
861 | #endif |
862 | |
863 | QT_END_NAMESPACE |
864 | |
865 | #include "moc_qquickslider_p.cpp" |
866 | |