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 "qquickrangeslider_p.h" |
5 | #include "qquickcontrol_p_p.h" |
6 | #include "qquickdeferredexecute_p_p.h" |
7 | |
8 | #include <QtCore/qscopedpointer.h> |
9 | #include <QtQuick/private/qquickwindow_p.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \qmltype RangeSlider |
15 | \inherits Control |
16 | //! \instantiates QQuickRangeSlider |
17 | \inqmlmodule QtQuick.Controls |
18 | \since 5.7 |
19 | \ingroup qtquickcontrols-input |
20 | \ingroup qtquickcontrols-focusscopes |
21 | \brief Used to select a range of values by sliding two handles along a track. |
22 | |
23 | \image qtquickcontrols-rangeslider.gif |
24 | |
25 | RangeSlider is used to select a range specified by two values, by sliding |
26 | each handle along a track. |
27 | |
28 | In the example below, custom \l from and \l to values are set, and the |
29 | initial positions of the \l first and \l second handles are set: |
30 | |
31 | \code |
32 | RangeSlider { |
33 | from: 1 |
34 | to: 100 |
35 | first.value: 25 |
36 | second.value: 75 |
37 | } |
38 | \endcode |
39 | |
40 | In order to perform an action when the value for a particular handle changes, |
41 | use the following syntax: |
42 | |
43 | \code |
44 | first.onMoved: console.log("first.value changed to " + first.value) |
45 | \endcode |
46 | |
47 | The \l {first.position} and \l {second.position} properties are expressed as |
48 | fractions of the control's size, in the range \c {0.0 - 1.0}. |
49 | The \l {first.visualPosition} and \l {second.visualPosition} properties are |
50 | the same, except that they are reversed in a |
51 | \l {Right-to-left User Interfaces}{right-to-left} application. |
52 | The \c visualPosition is useful for positioning the handles when styling |
53 | RangeSlider. In the example above, \l {first.visualPosition} will be \c 0.24 |
54 | in a left-to-right application, and \c 0.76 in a right-to-left application. |
55 | |
56 | For a slider that allows the user to select a single value, see \l Slider. |
57 | |
58 | \sa {Customizing RangeSlider}, {Input Controls}, |
59 | {Focus Management in Qt Quick Controls} |
60 | */ |
61 | |
62 | class QQuickRangeSliderNodePrivate : public QObjectPrivate |
63 | { |
64 | Q_DECLARE_PUBLIC(QQuickRangeSliderNode) |
65 | public: |
66 | QQuickRangeSliderNodePrivate(qreal value, QQuickRangeSlider *slider) |
67 | : value(value), |
68 | slider(slider) |
69 | { |
70 | } |
71 | |
72 | bool isFirst() const; |
73 | |
74 | void setPosition(qreal position, bool ignoreOtherPosition = false); |
75 | void updatePosition(bool ignoreOtherPosition = false); |
76 | |
77 | void cancelHandle(); |
78 | void executeHandle(bool complete = false); |
79 | |
80 | static QQuickRangeSliderNodePrivate *get(QQuickRangeSliderNode *node); |
81 | |
82 | qreal value = 0; |
83 | bool isPendingValue = false; |
84 | qreal pendingValue = 0; |
85 | qreal position = 0; |
86 | QQuickDeferredPointer<QQuickItem> handle; |
87 | QQuickRangeSlider *slider = nullptr; |
88 | bool pressed = false; |
89 | bool hovered = false; |
90 | int touchId = -1; |
91 | }; |
92 | |
93 | bool QQuickRangeSliderNodePrivate::isFirst() const |
94 | { |
95 | return this == get(node: slider->first()); |
96 | } |
97 | |
98 | void QQuickRangeSliderNodePrivate::setPosition(qreal position, bool ignoreOtherPosition) |
99 | { |
100 | Q_Q(QQuickRangeSliderNode); |
101 | |
102 | const qreal min = isFirst() || ignoreOtherPosition ? 0.0 : qMax<qreal>(a: 0.0, b: slider->first()->position()); |
103 | const qreal max = !isFirst() || ignoreOtherPosition ? 1.0 : qMin<qreal>(a: 1.0, b: slider->second()->position()); |
104 | position = qBound(min, val: position, max); |
105 | if (!qFuzzyCompare(p1: this->position, p2: position)) { |
106 | this->position = position; |
107 | emit q->positionChanged(); |
108 | emit q->visualPositionChanged(); |
109 | } |
110 | } |
111 | |
112 | void QQuickRangeSliderNodePrivate::updatePosition(bool ignoreOtherPosition) |
113 | { |
114 | qreal pos = 0; |
115 | if (!qFuzzyCompare(p1: slider->from(), p2: slider->to())) |
116 | pos = (value - slider->from()) / (slider->to() - slider->from()); |
117 | setPosition(position: pos, ignoreOtherPosition); |
118 | } |
119 | |
120 | void QQuickRangeSliderNodePrivate::cancelHandle() |
121 | { |
122 | Q_Q(QQuickRangeSliderNode); |
123 | quickCancelDeferred(object: q, property: handleName()); |
124 | } |
125 | |
126 | void QQuickRangeSliderNodePrivate::executeHandle(bool complete) |
127 | { |
128 | Q_Q(QQuickRangeSliderNode); |
129 | if (handle.wasExecuted()) |
130 | return; |
131 | |
132 | if (!handle || complete) |
133 | quickBeginDeferred(object: q, property: handleName(), delegate&: handle); |
134 | if (complete) |
135 | quickCompleteDeferred(object: q, property: handleName(), delegate&: handle); |
136 | } |
137 | |
138 | QQuickRangeSliderNodePrivate *QQuickRangeSliderNodePrivate::get(QQuickRangeSliderNode *node) |
139 | { |
140 | return node->d_func(); |
141 | } |
142 | |
143 | QQuickRangeSliderNode::QQuickRangeSliderNode(qreal value, QQuickRangeSlider *slider) |
144 | : QObject(*(new QQuickRangeSliderNodePrivate(value, slider)), slider) |
145 | { |
146 | } |
147 | |
148 | QQuickRangeSliderNode::~QQuickRangeSliderNode() |
149 | { |
150 | } |
151 | |
152 | qreal QQuickRangeSliderNode::value() const |
153 | { |
154 | Q_D(const QQuickRangeSliderNode); |
155 | return d->value; |
156 | } |
157 | |
158 | void QQuickRangeSliderNode::setValue(qreal value) |
159 | { |
160 | Q_D(QQuickRangeSliderNode); |
161 | if (!d->slider->isComponentComplete()) { |
162 | d->pendingValue = value; |
163 | d->isPendingValue = true; |
164 | return; |
165 | } |
166 | |
167 | // First, restrict the first value to be within to and from. |
168 | const qreal smaller = qMin(a: d->slider->to(), b: d->slider->from()); |
169 | const qreal larger = qMax(a: d->slider->to(), b: d->slider->from()); |
170 | value = qBound(min: smaller, val: value, max: larger); |
171 | |
172 | // Then, ensure that it doesn't go past the other value, |
173 | // a check that depends on whether or not the range is inverted. |
174 | const bool invertedRange = d->slider->from() > d->slider->to(); |
175 | if (d->isFirst()) { |
176 | if (invertedRange) { |
177 | if (value < d->slider->second()->value()) |
178 | value = d->slider->second()->value(); |
179 | } else { |
180 | if (value > d->slider->second()->value()) |
181 | value = d->slider->second()->value(); |
182 | } |
183 | } else { |
184 | if (invertedRange) { |
185 | if (value > d->slider->first()->value()) |
186 | value = d->slider->first()->value(); |
187 | } else { |
188 | if (value < d->slider->first()->value()) |
189 | value = d->slider->first()->value(); |
190 | } |
191 | } |
192 | |
193 | if (!qFuzzyCompare(p1: d->value, p2: value)) { |
194 | d->value = value; |
195 | d->updatePosition(); |
196 | emit valueChanged(); |
197 | } |
198 | } |
199 | |
200 | qreal QQuickRangeSliderNode::position() const |
201 | { |
202 | Q_D(const QQuickRangeSliderNode); |
203 | return d->position; |
204 | } |
205 | |
206 | qreal QQuickRangeSliderNode::visualPosition() const |
207 | { |
208 | Q_D(const QQuickRangeSliderNode); |
209 | if (d->slider->orientation() == Qt::Vertical || d->slider->isMirrored()) |
210 | return 1.0 - d->position; |
211 | return d->position; |
212 | } |
213 | |
214 | QQuickItem *QQuickRangeSliderNode::handle() const |
215 | { |
216 | QQuickRangeSliderNodePrivate *d = const_cast<QQuickRangeSliderNodePrivate *>(d_func()); |
217 | if (!d->handle) |
218 | d->executeHandle(); |
219 | return d->handle; |
220 | } |
221 | |
222 | void QQuickRangeSliderNode::setHandle(QQuickItem *handle) |
223 | { |
224 | Q_D(QQuickRangeSliderNode); |
225 | if (d->handle == handle) |
226 | return; |
227 | |
228 | if (!d->handle.isExecuting()) |
229 | d->cancelHandle(); |
230 | |
231 | const qreal oldImplicitHandleWidth = implicitHandleWidth(); |
232 | const qreal oldImplicitHandleHeight = implicitHandleHeight(); |
233 | |
234 | QQuickControlPrivate::get(control: d->slider)->removeImplicitSizeListener(item: d->handle); |
235 | QQuickControlPrivate::hideOldItem(item: d->handle); |
236 | d->handle = handle; |
237 | |
238 | if (handle) { |
239 | if (!handle->parentItem()) |
240 | handle->setParentItem(d->slider); |
241 | |
242 | QQuickItem *firstHandle = QQuickRangeSliderNodePrivate::get(node: d->slider->first())->handle; |
243 | QQuickItem *secondHandle = QQuickRangeSliderNodePrivate::get(node: d->slider->second())->handle; |
244 | if (firstHandle && secondHandle) { |
245 | // The order of property assignments in QML is undefined, |
246 | // but we need the first handle to be before the second due |
247 | // to focus order constraints, so check for that here. |
248 | const QList<QQuickItem *> childItems = d->slider->childItems(); |
249 | const int firstIndex = childItems.indexOf(t: firstHandle); |
250 | const int secondIndex = childItems.indexOf(t: secondHandle); |
251 | if (firstIndex != -1 && secondIndex != -1 && firstIndex > secondIndex) { |
252 | firstHandle->stackBefore(secondHandle); |
253 | // Ensure we have some way of knowing which handle is above |
254 | // the other when it comes to mouse presses, and also that |
255 | // they are rendered in the correct order. |
256 | secondHandle->setZ(secondHandle->z() + 1); |
257 | } |
258 | } |
259 | |
260 | handle->setActiveFocusOnTab(true); |
261 | QQuickControlPrivate::get(control: d->slider)->addImplicitSizeListener(item: handle); |
262 | } |
263 | |
264 | if (!qFuzzyCompare(p1: oldImplicitHandleWidth, p2: implicitHandleWidth())) |
265 | emit implicitHandleWidthChanged(); |
266 | if (!qFuzzyCompare(p1: oldImplicitHandleHeight, p2: implicitHandleHeight())) |
267 | emit implicitHandleHeightChanged(); |
268 | if (!d->handle.isExecuting()) |
269 | emit handleChanged(); |
270 | } |
271 | |
272 | bool QQuickRangeSliderNode::isPressed() const |
273 | { |
274 | Q_D(const QQuickRangeSliderNode); |
275 | return d->pressed; |
276 | } |
277 | |
278 | void QQuickRangeSliderNode::setPressed(bool pressed) |
279 | { |
280 | Q_D(QQuickRangeSliderNode); |
281 | if (d->pressed == pressed) |
282 | return; |
283 | |
284 | d->pressed = pressed; |
285 | d->slider->setAccessibleProperty(propertyName: "pressed" , value: pressed || d->slider->second()->isPressed()); |
286 | emit pressedChanged(); |
287 | } |
288 | |
289 | bool QQuickRangeSliderNode::isHovered() const |
290 | { |
291 | Q_D(const QQuickRangeSliderNode); |
292 | return d->hovered; |
293 | } |
294 | |
295 | void QQuickRangeSliderNode::setHovered(bool hovered) |
296 | { |
297 | Q_D(QQuickRangeSliderNode); |
298 | if (d->hovered == hovered) |
299 | return; |
300 | |
301 | d->hovered = hovered; |
302 | emit hoveredChanged(); |
303 | } |
304 | |
305 | qreal QQuickRangeSliderNode::implicitHandleWidth() const |
306 | { |
307 | Q_D(const QQuickRangeSliderNode); |
308 | if (!d->handle) |
309 | return 0; |
310 | return d->handle->implicitWidth(); |
311 | } |
312 | |
313 | qreal QQuickRangeSliderNode::implicitHandleHeight() const |
314 | { |
315 | Q_D(const QQuickRangeSliderNode); |
316 | if (!d->handle) |
317 | return 0; |
318 | return d->handle->implicitHeight(); |
319 | } |
320 | |
321 | void QQuickRangeSliderNode::increase() |
322 | { |
323 | Q_D(QQuickRangeSliderNode); |
324 | qreal step = qFuzzyIsNull(d: d->slider->stepSize()) ? 0.1 : d->slider->stepSize(); |
325 | setValue(d->value + step); |
326 | } |
327 | |
328 | void QQuickRangeSliderNode::decrease() |
329 | { |
330 | Q_D(QQuickRangeSliderNode); |
331 | qreal step = qFuzzyIsNull(d: d->slider->stepSize()) ? 0.1 : d->slider->stepSize(); |
332 | setValue(d->value - step); |
333 | } |
334 | |
335 | static const qreal defaultFrom = 0.0; |
336 | static const qreal defaultTo = 1.0; |
337 | |
338 | class QQuickRangeSliderPrivate : public QQuickControlPrivate |
339 | { |
340 | Q_DECLARE_PUBLIC(QQuickRangeSlider) |
341 | |
342 | public: |
343 | QQuickRangeSliderNode *pressedNode(int touchId = -1) const; |
344 | |
345 | #if QT_CONFIG(quicktemplates2_multitouch) |
346 | bool acceptTouch(const QTouchEvent::TouchPoint &point) override; |
347 | #endif |
348 | bool handlePress(const QPointF &point, ulong timestamp) override; |
349 | bool handleMove(const QPointF &point, ulong timestamp) override; |
350 | bool handleRelease(const QPointF &point, ulong timestamp) override; |
351 | void handleUngrab() override; |
352 | |
353 | void updateHover(const QPointF &pos); |
354 | |
355 | void itemImplicitWidthChanged(QQuickItem *item) override; |
356 | void itemImplicitHeightChanged(QQuickItem *item) override; |
357 | |
358 | bool live = true; |
359 | qreal from = defaultFrom; |
360 | qreal to = defaultTo; |
361 | qreal stepSize = 0; |
362 | qreal touchDragThreshold = -1; |
363 | QQuickRangeSliderNode *first = nullptr; |
364 | QQuickRangeSliderNode *second = nullptr; |
365 | QPointF pressPoint; |
366 | Qt::Orientation orientation = Qt::Horizontal; |
367 | QQuickRangeSlider::SnapMode snapMode = QQuickRangeSlider::NoSnap; |
368 | }; |
369 | |
370 | static qreal valueAt(const QQuickRangeSlider *slider, qreal position) |
371 | { |
372 | return slider->from() + (slider->to() - slider->from()) * position; |
373 | } |
374 | |
375 | static qreal snapPosition(const QQuickRangeSlider *slider, qreal position) |
376 | { |
377 | const qreal range = slider->to() - slider->from(); |
378 | if (qFuzzyIsNull(d: range)) |
379 | return position; |
380 | |
381 | const qreal effectiveStep = slider->stepSize() / range; |
382 | if (qFuzzyIsNull(d: effectiveStep)) |
383 | return position; |
384 | |
385 | return qRound(d: position / effectiveStep) * effectiveStep; |
386 | } |
387 | |
388 | static qreal positionAt(const QQuickRangeSlider *slider, QQuickItem *handle, const QPointF &point) |
389 | { |
390 | if (slider->orientation() == Qt::Horizontal) { |
391 | const qreal hw = handle ? handle->width() : 0; |
392 | const qreal offset = hw / 2; |
393 | const qreal extent = slider->availableWidth() - hw; |
394 | if (!qFuzzyIsNull(d: extent)) { |
395 | if (slider->isMirrored()) |
396 | return (slider->width() - point.x() - slider->rightPadding() - offset) / extent; |
397 | return (point.x() - slider->leftPadding() - offset) / extent; |
398 | } |
399 | } else { |
400 | const qreal hh = handle ? handle->height() : 0; |
401 | const qreal offset = hh / 2; |
402 | const qreal extent = slider->availableHeight() - hh; |
403 | if (!qFuzzyIsNull(d: extent)) |
404 | return (slider->height() - point.y() - slider->bottomPadding() - offset) / extent; |
405 | } |
406 | return 0; |
407 | } |
408 | |
409 | QQuickRangeSliderNode *QQuickRangeSliderPrivate::pressedNode(int touchId) const |
410 | { |
411 | if (touchId == -1) |
412 | return first->isPressed() ? first : (second->isPressed() ? second : nullptr); |
413 | if (QQuickRangeSliderNodePrivate::get(node: first)->touchId == touchId) |
414 | return first; |
415 | if (QQuickRangeSliderNodePrivate::get(node: second)->touchId == touchId) |
416 | return second; |
417 | return nullptr; |
418 | } |
419 | |
420 | #if QT_CONFIG(quicktemplates2_multitouch) |
421 | bool QQuickRangeSliderPrivate::acceptTouch(const QTouchEvent::TouchPoint &point) |
422 | { |
423 | int firstId = QQuickRangeSliderNodePrivate::get(node: first)->touchId; |
424 | int secondId = QQuickRangeSliderNodePrivate::get(node: second)->touchId; |
425 | |
426 | if (((firstId == -1 || secondId == -1) && point.state() == QEventPoint::Pressed) || point.id() == firstId || point.id() == secondId) { |
427 | touchId = point.id(); |
428 | return true; |
429 | } |
430 | |
431 | return false; |
432 | } |
433 | #endif |
434 | |
435 | bool QQuickRangeSliderPrivate::handlePress(const QPointF &point, ulong timestamp) |
436 | { |
437 | Q_Q(QQuickRangeSlider); |
438 | QQuickControlPrivate::handlePress(point, timestamp); |
439 | pressPoint = point; |
440 | |
441 | QQuickItem *firstHandle = first->handle(); |
442 | QQuickItem *secondHandle = second->handle(); |
443 | const bool firstHit = firstHandle && !first->isPressed() && firstHandle->contains(point: q->mapToItem(item: firstHandle, point)); |
444 | const bool secondHit = secondHandle && !second->isPressed() && secondHandle->contains(point: q->mapToItem(item: secondHandle, point)); |
445 | QQuickRangeSliderNode *hitNode = nullptr; |
446 | QQuickRangeSliderNode *otherNode = nullptr; |
447 | |
448 | if (firstHit && secondHit) { |
449 | // choose highest |
450 | hitNode = firstHandle->z() > secondHandle->z() ? first : second; |
451 | otherNode = firstHandle->z() > secondHandle->z() ? second : first; |
452 | } else if (firstHit) { |
453 | hitNode = first; |
454 | otherNode = second; |
455 | } else if (secondHit) { |
456 | hitNode = second; |
457 | otherNode = first; |
458 | } else { |
459 | // find the nearest |
460 | const qreal firstPos = positionAt(slider: q, handle: firstHandle, point); |
461 | const qreal secondPos = positionAt(slider: q, handle: secondHandle, point); |
462 | const qreal firstDistance = qAbs(t: firstPos - first->position()); |
463 | const qreal secondDistance = qAbs(t: secondPos - second->position()); |
464 | |
465 | if (qFuzzyCompare(p1: firstDistance, p2: secondDistance)) { |
466 | // same distance => choose the one that can be moved towards the press position |
467 | const bool inverted = from > to; |
468 | if ((!inverted && firstPos < first->position()) || (inverted && firstPos > first->position())) { |
469 | hitNode = first; |
470 | otherNode = second; |
471 | } else { |
472 | hitNode = second; |
473 | otherNode = first; |
474 | } |
475 | } else if (firstDistance < secondDistance) { |
476 | hitNode = first; |
477 | otherNode = second; |
478 | } else { |
479 | hitNode = second; |
480 | otherNode = first; |
481 | } |
482 | } |
483 | |
484 | if (hitNode) { |
485 | hitNode->setPressed(true); |
486 | if (QQuickItem *handle = hitNode->handle()) |
487 | handle->setZ(1); |
488 | QQuickRangeSliderNodePrivate::get(node: hitNode)->touchId = touchId; |
489 | } |
490 | if (otherNode) { |
491 | if (QQuickItem *handle = otherNode->handle()) |
492 | handle->setZ(0); |
493 | } |
494 | return true; |
495 | } |
496 | |
497 | bool QQuickRangeSliderPrivate::handleMove(const QPointF &point, ulong timestamp) |
498 | { |
499 | Q_Q(QQuickRangeSlider); |
500 | QQuickControlPrivate::handleMove(point, timestamp); |
501 | QQuickRangeSliderNode *pressedNode = QQuickRangeSliderPrivate::pressedNode(touchId); |
502 | if (pressedNode) { |
503 | const qreal oldPos = pressedNode->position(); |
504 | qreal pos = positionAt(slider: q, handle: pressedNode->handle(), point); |
505 | if (snapMode == QQuickRangeSlider::SnapAlways) |
506 | pos = snapPosition(slider: q, position: pos); |
507 | if (live) |
508 | pressedNode->setValue(valueAt(slider: q, position: pos)); |
509 | else |
510 | QQuickRangeSliderNodePrivate::get(node: pressedNode)->setPosition(position: pos); |
511 | |
512 | if (!qFuzzyCompare(p1: pressedNode->position(), p2: oldPos)) |
513 | emit pressedNode->moved(); |
514 | } |
515 | return true; |
516 | } |
517 | |
518 | bool QQuickRangeSliderPrivate::handleRelease(const QPointF &point, ulong timestamp) |
519 | { |
520 | Q_Q(QQuickRangeSlider); |
521 | QQuickControlPrivate::handleRelease(point, timestamp); |
522 | pressPoint = QPointF(); |
523 | |
524 | QQuickRangeSliderNode *pressedNode = QQuickRangeSliderPrivate::pressedNode(touchId); |
525 | if (!pressedNode) |
526 | return true; |
527 | QQuickRangeSliderNodePrivate *pressedNodePrivate = QQuickRangeSliderNodePrivate::get(node: pressedNode); |
528 | |
529 | const qreal oldPos = pressedNode->position(); |
530 | qreal pos = positionAt(slider: q, handle: pressedNode->handle(), point); |
531 | if (snapMode != QQuickRangeSlider::NoSnap) |
532 | pos = snapPosition(slider: q, position: pos); |
533 | qreal val = valueAt(slider: q, position: pos); |
534 | if (!qFuzzyCompare(p1: val, p2: pressedNode->value())) |
535 | pressedNode->setValue(val); |
536 | else if (snapMode != QQuickRangeSlider::NoSnap) |
537 | pressedNodePrivate->setPosition(position: pos); |
538 | q->setKeepMouseGrab(false); |
539 | q->setKeepTouchGrab(false); |
540 | |
541 | if (!qFuzzyCompare(p1: pressedNode->position(), p2: oldPos)) |
542 | emit pressedNode->moved(); |
543 | |
544 | pressedNode->setPressed(false); |
545 | pressedNodePrivate->touchId = -1; |
546 | return true; |
547 | } |
548 | |
549 | void QQuickRangeSliderPrivate::handleUngrab() |
550 | { |
551 | QQuickControlPrivate::handleUngrab(); |
552 | pressPoint = QPointF(); |
553 | first->setPressed(false); |
554 | second->setPressed(false); |
555 | QQuickRangeSliderNodePrivate::get(node: first)->touchId = -1; |
556 | QQuickRangeSliderNodePrivate::get(node: second)->touchId = -1; |
557 | } |
558 | |
559 | void QQuickRangeSliderPrivate::updateHover(const QPointF &pos) |
560 | { |
561 | Q_Q(QQuickRangeSlider); |
562 | QQuickItem *firstHandle = first->handle(); |
563 | QQuickItem *secondHandle = second->handle(); |
564 | bool firstHandleHovered = firstHandle && firstHandle->isEnabled() |
565 | && firstHandle->contains(point: q->mapToItem(item: firstHandle, point: pos)); |
566 | bool secondHandleHovered = secondHandle && secondHandle->isEnabled() |
567 | && secondHandle->contains(point: q->mapToItem(item: secondHandle, point: pos)); |
568 | |
569 | if (firstHandleHovered && secondHandleHovered) { |
570 | // Only hover the handle with the higher Z value. |
571 | const bool firstHandleHasHigherZ = firstHandle->z() > secondHandle->z(); |
572 | firstHandleHovered = firstHandleHasHigherZ; |
573 | secondHandleHovered = !firstHandleHasHigherZ; |
574 | } |
575 | first->setHovered(firstHandleHovered); |
576 | second->setHovered(secondHandleHovered); |
577 | } |
578 | |
579 | void QQuickRangeSliderPrivate::itemImplicitWidthChanged(QQuickItem *item) |
580 | { |
581 | QQuickControlPrivate::itemImplicitWidthChanged(item); |
582 | if (item == first->handle()) |
583 | emit first->implicitHandleWidthChanged(); |
584 | else if (item == second->handle()) |
585 | emit second->implicitHandleWidthChanged(); |
586 | } |
587 | |
588 | void QQuickRangeSliderPrivate::itemImplicitHeightChanged(QQuickItem *item) |
589 | { |
590 | QQuickControlPrivate::itemImplicitHeightChanged(item); |
591 | if (item == first->handle()) |
592 | emit first->implicitHandleHeightChanged(); |
593 | else if (item == second->handle()) |
594 | emit second->implicitHandleHeightChanged(); |
595 | } |
596 | |
597 | QQuickRangeSlider::QQuickRangeSlider(QQuickItem *parent) |
598 | : QQuickControl(*(new QQuickRangeSliderPrivate), parent) |
599 | { |
600 | Q_D(QQuickRangeSlider); |
601 | d->first = new QQuickRangeSliderNode(0.0, this); |
602 | d->second = new QQuickRangeSliderNode(1.0, this); |
603 | |
604 | setFlag(flag: QQuickItem::ItemIsFocusScope); |
605 | setAcceptedMouseButtons(Qt::LeftButton); |
606 | #if QT_CONFIG(quicktemplates2_multitouch) |
607 | setAcceptTouchEvents(true); |
608 | #endif |
609 | #if QT_CONFIG(cursor) |
610 | setCursor(Qt::ArrowCursor); |
611 | #endif |
612 | } |
613 | |
614 | QQuickRangeSlider::~QQuickRangeSlider() |
615 | { |
616 | Q_D(QQuickRangeSlider); |
617 | d->removeImplicitSizeListener(item: d->first->handle()); |
618 | d->removeImplicitSizeListener(item: d->second->handle()); |
619 | } |
620 | |
621 | /*! |
622 | \qmlproperty real QtQuick.Controls::RangeSlider::from |
623 | |
624 | This property holds the starting value for the range. The default value is \c 0.0. |
625 | |
626 | \sa to, first.value, second.value |
627 | */ |
628 | qreal QQuickRangeSlider::from() const |
629 | { |
630 | Q_D(const QQuickRangeSlider); |
631 | return d->from; |
632 | } |
633 | |
634 | void QQuickRangeSlider::setFrom(qreal from) |
635 | { |
636 | Q_D(QQuickRangeSlider); |
637 | if (qFuzzyCompare(p1: d->from, p2: from)) |
638 | return; |
639 | |
640 | d->from = from; |
641 | emit fromChanged(); |
642 | |
643 | if (isComponentComplete()) { |
644 | d->first->setValue(d->first->value()); |
645 | d->second->setValue(d->second->value()); |
646 | auto *firstPrivate = QQuickRangeSliderNodePrivate::get(node: d->first); |
647 | auto *secondPrivate = QQuickRangeSliderNodePrivate::get(node: d->second); |
648 | firstPrivate->updatePosition(ignoreOtherPosition: true); |
649 | secondPrivate->updatePosition(); |
650 | } |
651 | } |
652 | |
653 | /*! |
654 | \qmlproperty real QtQuick.Controls::RangeSlider::to |
655 | |
656 | This property holds the end value for the range. The default value is \c 1.0. |
657 | |
658 | \sa from, first.value, second.value |
659 | */ |
660 | qreal QQuickRangeSlider::to() const |
661 | { |
662 | Q_D(const QQuickRangeSlider); |
663 | return d->to; |
664 | } |
665 | |
666 | void QQuickRangeSlider::setTo(qreal to) |
667 | { |
668 | Q_D(QQuickRangeSlider); |
669 | if (qFuzzyCompare(p1: d->to, p2: to)) |
670 | return; |
671 | |
672 | d->to = to; |
673 | emit toChanged(); |
674 | |
675 | if (isComponentComplete()) { |
676 | d->first->setValue(d->first->value()); |
677 | d->second->setValue(d->second->value()); |
678 | auto *firstPrivate = QQuickRangeSliderNodePrivate::get(node: d->first); |
679 | auto *secondPrivate = QQuickRangeSliderNodePrivate::get(node: d->second); |
680 | firstPrivate->updatePosition(ignoreOtherPosition: true); |
681 | secondPrivate->updatePosition(); |
682 | } |
683 | } |
684 | |
685 | /*! |
686 | \since QtQuick.Controls 2.5 (Qt 5.12) |
687 | \qmlproperty qreal QtQuick.Controls::RangeSlider::touchDragThreshold |
688 | |
689 | This property holds the threshold (in logical pixels) at which a touch drag event will be initiated. |
690 | The mouse drag threshold won't be affected. |
691 | The default value is \c Qt.styleHints.startDragDistance. |
692 | |
693 | \sa QStyleHints |
694 | |
695 | */ |
696 | qreal QQuickRangeSlider::touchDragThreshold() const |
697 | { |
698 | Q_D(const QQuickRangeSlider); |
699 | return d->touchDragThreshold; |
700 | } |
701 | |
702 | void QQuickRangeSlider::setTouchDragThreshold(qreal touchDragThreshold) |
703 | { |
704 | Q_D(QQuickRangeSlider); |
705 | if (d->touchDragThreshold == touchDragThreshold) |
706 | return; |
707 | |
708 | d->touchDragThreshold = touchDragThreshold; |
709 | emit touchDragThresholdChanged(); |
710 | } |
711 | |
712 | void QQuickRangeSlider::resetTouchDragThreshold() |
713 | { |
714 | setTouchDragThreshold(-1); |
715 | } |
716 | |
717 | /*! |
718 | \since QtQuick.Controls 2.5 (Qt 5.12) |
719 | \qmlmethod real QtQuick.Controls::RangeSlider::valueAt(real position) |
720 | |
721 | Returns the value for the given \a position. |
722 | |
723 | \sa first.value, second.value, first.position, second.position, live |
724 | */ |
725 | qreal QQuickRangeSlider::valueAt(qreal position) const |
726 | { |
727 | Q_D(const QQuickRangeSlider); |
728 | const qreal value = (d->to - d->from) * position; |
729 | if (qFuzzyIsNull(d: d->stepSize)) |
730 | return d->from + value; |
731 | return d->from + qRound(d: value / d->stepSize) * d->stepSize; |
732 | } |
733 | |
734 | /*! |
735 | \qmlproperty real QtQuick.Controls::RangeSlider::first.value |
736 | \qmlproperty real QtQuick.Controls::RangeSlider::first.position |
737 | \qmlproperty real QtQuick.Controls::RangeSlider::first.visualPosition |
738 | \qmlproperty Item QtQuick.Controls::RangeSlider::first.handle |
739 | \qmlproperty bool QtQuick.Controls::RangeSlider::first.pressed |
740 | \qmlproperty bool QtQuick.Controls::RangeSlider::first.hovered |
741 | \qmlproperty real QtQuick.Controls::RangeSlider::first.implicitHandleWidth |
742 | \qmlproperty real QtQuick.Controls::RangeSlider::first.implicitHandleHeight |
743 | |
744 | \table |
745 | \header |
746 | \li Property |
747 | \li Description |
748 | \row |
749 | \li value |
750 | \li This property holds the value of the first handle in the range |
751 | \c from - \c to. |
752 | |
753 | If \l from is greater than \l to, the value of the first handle |
754 | must be greater than the second, and vice versa. |
755 | |
756 | The default value is \c 0.0. |
757 | \row |
758 | \li handle |
759 | \li This property holds the first handle item. |
760 | \row |
761 | \li visualPosition |
762 | \li This property holds the visual position of the first handle. |
763 | |
764 | The position is expressed as a fraction of the control's size, in the range |
765 | \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the |
766 | value is equal to \c {1.0 - position}. This makes the value suitable for |
767 | visualizing the slider, taking right-to-left support into account. |
768 | \row |
769 | \li position |
770 | \li This property holds the logical position of the first handle. |
771 | |
772 | The position is expressed as a fraction of the control's size, in the range |
773 | \c {0.0 - 1.0}. For visualizing a slider, the right-to-left aware |
774 | \l {first.visualPosition}{visualPosition} should be used instead. |
775 | \row |
776 | \li pressed |
777 | \li This property holds whether the first handle is pressed by either touch, |
778 | mouse, or keys. |
779 | \row |
780 | \li hovered |
781 | \li This property holds whether the first handle is hovered. |
782 | This property was introduced in QtQuick.Controls 2.1. |
783 | \row |
784 | \li implicitHandleWidth |
785 | \li This property holds the implicit width of the first handle. |
786 | This property was introduced in QtQuick.Controls 2.5. |
787 | \row |
788 | \li implicitHandleHeight |
789 | \li This property holds the implicit height of the first handle. |
790 | This property was introduced in QtQuick.Controls 2.5. |
791 | \endtable |
792 | |
793 | \sa first.moved(), first.increase(), first.decrease() |
794 | */ |
795 | QQuickRangeSliderNode *QQuickRangeSlider::first() const |
796 | { |
797 | Q_D(const QQuickRangeSlider); |
798 | return d->first; |
799 | } |
800 | |
801 | /*! |
802 | \qmlsignal void QtQuick.Controls::RangeSlider::first.moved() |
803 | \qmlsignal void QtQuick.Controls::RangeSlider::second.moved() |
804 | \since QtQuick.Controls 2.5 |
805 | |
806 | This signal is emitted when either the first or second handle has been |
807 | interactively moved by the user by either touch, mouse, or keys. |
808 | |
809 | \sa first, second |
810 | */ |
811 | |
812 | /*! |
813 | \qmlproperty real QtQuick.Controls::RangeSlider::second.value |
814 | \qmlproperty real QtQuick.Controls::RangeSlider::second.position |
815 | \qmlproperty real QtQuick.Controls::RangeSlider::second.visualPosition |
816 | \qmlproperty Item QtQuick.Controls::RangeSlider::second.handle |
817 | \qmlproperty bool QtQuick.Controls::RangeSlider::second.pressed |
818 | \qmlproperty bool QtQuick.Controls::RangeSlider::second.hovered |
819 | \qmlproperty real QtQuick.Controls::RangeSlider::second.implicitHandleWidth |
820 | \qmlproperty real QtQuick.Controls::RangeSlider::second.implicitHandleHeight |
821 | |
822 | \table |
823 | \header |
824 | \li Property |
825 | \li Description |
826 | \row |
827 | \li value |
828 | \li This property holds the value of the second handle in the range |
829 | \c from - \c to. |
830 | |
831 | If \l from is greater than \l to, the value of the first handle |
832 | must be greater than the second, and vice versa. |
833 | |
834 | The default value is \c 0.0. |
835 | \row |
836 | \li handle |
837 | \li This property holds the second handle item. |
838 | \row |
839 | \li visualPosition |
840 | \li This property holds the visual position of the second handle. |
841 | |
842 | The position is expressed as a fraction of the control's size, in the range |
843 | \c {0.0 - 1.0}. When the control is \l {Control::mirrored}{mirrored}, the |
844 | value is equal to \c {1.0 - position}. This makes the value suitable for |
845 | visualizing the slider, taking right-to-left support into account. |
846 | \row |
847 | \li position |
848 | \li This property holds the logical position of the second handle. |
849 | |
850 | The position is expressed as a fraction of the control's size, in the range |
851 | \c {0.0 - 1.0}. For visualizing a slider, the right-to-left aware |
852 | \l {second.visualPosition}{visualPosition} should be used instead. |
853 | \row |
854 | \li pressed |
855 | \li This property holds whether the second handle is pressed by either touch, |
856 | mouse, or keys. |
857 | \row |
858 | \li hovered |
859 | \li This property holds whether the second handle is hovered. |
860 | This property was introduced in QtQuick.Controls 2.1. |
861 | \row |
862 | \li implicitHandleWidth |
863 | \li This property holds the implicit width of the second handle. |
864 | This property was introduced in QtQuick.Controls 2.5. |
865 | \row |
866 | \li implicitHandleHeight |
867 | \li This property holds the implicit height of the second handle. |
868 | This property was introduced in QtQuick.Controls 2.5. |
869 | \endtable |
870 | |
871 | \sa second.moved(), second.increase(), second.decrease() |
872 | */ |
873 | QQuickRangeSliderNode *QQuickRangeSlider::second() const |
874 | { |
875 | Q_D(const QQuickRangeSlider); |
876 | return d->second; |
877 | } |
878 | |
879 | /*! |
880 | \qmlproperty real QtQuick.Controls::RangeSlider::stepSize |
881 | |
882 | This property holds the step size. The default value is \c 0.0. |
883 | |
884 | \sa snapMode, first.increase(), first.decrease() |
885 | */ |
886 | qreal QQuickRangeSlider::stepSize() const |
887 | { |
888 | Q_D(const QQuickRangeSlider); |
889 | return d->stepSize; |
890 | } |
891 | |
892 | void QQuickRangeSlider::setStepSize(qreal step) |
893 | { |
894 | Q_D(QQuickRangeSlider); |
895 | if (qFuzzyCompare(p1: d->stepSize, p2: step)) |
896 | return; |
897 | |
898 | d->stepSize = step; |
899 | emit stepSizeChanged(); |
900 | } |
901 | |
902 | /*! |
903 | \qmlproperty enumeration QtQuick.Controls::RangeSlider::snapMode |
904 | |
905 | This property holds the snap mode. |
906 | |
907 | The snap mode determines how the slider handles behave with |
908 | regards to the \l stepSize. |
909 | |
910 | Possible values: |
911 | \value RangeSlider.NoSnap The slider does not snap (default). |
912 | \value RangeSlider.SnapAlways The slider snaps while the handle is dragged. |
913 | \value RangeSlider.SnapOnRelease The slider does not snap while being dragged, but only after the handle is released. |
914 | |
915 | For visual explanations of the various modes, see the |
916 | \l {Slider::}{snapMode} documentation of \l Slider. |
917 | |
918 | \sa stepSize |
919 | */ |
920 | QQuickRangeSlider::SnapMode QQuickRangeSlider::snapMode() const |
921 | { |
922 | Q_D(const QQuickRangeSlider); |
923 | return d->snapMode; |
924 | } |
925 | |
926 | void QQuickRangeSlider::setSnapMode(SnapMode mode) |
927 | { |
928 | Q_D(QQuickRangeSlider); |
929 | if (d->snapMode == mode) |
930 | return; |
931 | |
932 | d->snapMode = mode; |
933 | emit snapModeChanged(); |
934 | } |
935 | |
936 | /*! |
937 | \qmlproperty enumeration QtQuick.Controls::RangeSlider::orientation |
938 | |
939 | This property holds the orientation. |
940 | |
941 | Possible values: |
942 | \value Qt.Horizontal Horizontal (default) |
943 | \value Qt.Vertical Vertical |
944 | |
945 | \sa horizontal, vertical |
946 | */ |
947 | Qt::Orientation QQuickRangeSlider::orientation() const |
948 | { |
949 | Q_D(const QQuickRangeSlider); |
950 | return d->orientation; |
951 | } |
952 | |
953 | void QQuickRangeSlider::setOrientation(Qt::Orientation orientation) |
954 | { |
955 | Q_D(QQuickRangeSlider); |
956 | if (d->orientation == orientation) |
957 | return; |
958 | |
959 | d->orientation = orientation; |
960 | emit orientationChanged(); |
961 | } |
962 | |
963 | /*! |
964 | \qmlmethod void QtQuick.Controls::RangeSlider::setValues(real firstValue, real secondValue) |
965 | |
966 | Sets \l first.value and \l second.value with the given arguments. |
967 | |
968 | If \l to is larger than \l from and \a firstValue is larger than |
969 | \a secondValue, firstValue will be clamped to secondValue. |
970 | |
971 | If \l from is larger than \l to and secondValue is larger than |
972 | firstValue, secondValue will be clamped to firstValue. |
973 | |
974 | This function may be necessary to set the first and second values |
975 | after the control has been completed, as there is a circular |
976 | dependency between firstValue and secondValue which can cause |
977 | assigned values to be clamped to each other. |
978 | |
979 | \sa stepSize |
980 | */ |
981 | void QQuickRangeSlider::setValues(qreal firstValue, qreal secondValue) |
982 | { |
983 | Q_D(QQuickRangeSlider); |
984 | // Restrict the values to be within to and from. |
985 | const qreal smaller = qMin(a: d->to, b: d->from); |
986 | const qreal larger = qMax(a: d->to, b: d->from); |
987 | firstValue = qBound(min: smaller, val: firstValue, max: larger); |
988 | secondValue = qBound(min: smaller, val: secondValue, max: larger); |
989 | |
990 | if (d->from > d->to) { |
991 | // If the from and to values are reversed, the secondValue |
992 | // might be less than the first value, which is not allowed. |
993 | if (secondValue > firstValue) |
994 | secondValue = firstValue; |
995 | } else { |
996 | // Otherwise, clamp first to second if it's too large. |
997 | if (firstValue > secondValue) |
998 | firstValue = secondValue; |
999 | } |
1000 | |
1001 | // Then set both values. If they didn't change, no change signal will be emitted. |
1002 | QQuickRangeSliderNodePrivate *firstPrivate = QQuickRangeSliderNodePrivate::get(node: d->first); |
1003 | if (firstValue != firstPrivate->value) { |
1004 | firstPrivate->value = firstValue; |
1005 | emit d->first->valueChanged(); |
1006 | } |
1007 | |
1008 | QQuickRangeSliderNodePrivate *secondPrivate = QQuickRangeSliderNodePrivate::get(node: d->second); |
1009 | if (secondValue != secondPrivate->value) { |
1010 | secondPrivate->value = secondValue; |
1011 | emit d->second->valueChanged(); |
1012 | } |
1013 | |
1014 | // After we've set both values, then we can update the positions. |
1015 | // If we don't do this last, the positions may be incorrect. |
1016 | firstPrivate->updatePosition(ignoreOtherPosition: true); |
1017 | secondPrivate->updatePosition(); |
1018 | } |
1019 | |
1020 | /*! |
1021 | \since QtQuick.Controls 2.2 (Qt 5.9) |
1022 | \qmlproperty bool QtQuick.Controls::RangeSlider::live |
1023 | |
1024 | This property holds whether the slider provides live updates for the \l first.value |
1025 | and \l second.value properties while the respective handles are dragged. |
1026 | |
1027 | The default value is \c true. |
1028 | |
1029 | \sa first.value, second.value |
1030 | */ |
1031 | bool QQuickRangeSlider::live() const |
1032 | { |
1033 | Q_D(const QQuickRangeSlider); |
1034 | return d->live; |
1035 | } |
1036 | |
1037 | void QQuickRangeSlider::setLive(bool live) |
1038 | { |
1039 | Q_D(QQuickRangeSlider); |
1040 | if (d->live == live) |
1041 | return; |
1042 | |
1043 | d->live = live; |
1044 | emit liveChanged(); |
1045 | } |
1046 | |
1047 | /*! |
1048 | \since QtQuick.Controls 2.3 (Qt 5.10) |
1049 | \qmlproperty bool QtQuick.Controls::RangeSlider::horizontal |
1050 | \readonly |
1051 | |
1052 | This property holds whether the slider is horizontal. |
1053 | |
1054 | \sa orientation |
1055 | */ |
1056 | bool QQuickRangeSlider::isHorizontal() const |
1057 | { |
1058 | Q_D(const QQuickRangeSlider); |
1059 | return d->orientation == Qt::Horizontal; |
1060 | } |
1061 | |
1062 | /*! |
1063 | \since QtQuick.Controls 2.3 (Qt 5.10) |
1064 | \qmlproperty bool QtQuick.Controls::RangeSlider::vertical |
1065 | \readonly |
1066 | |
1067 | This property holds whether the slider is vertical. |
1068 | |
1069 | \sa orientation |
1070 | */ |
1071 | bool QQuickRangeSlider::isVertical() const |
1072 | { |
1073 | Q_D(const QQuickRangeSlider); |
1074 | return d->orientation == Qt::Vertical; |
1075 | } |
1076 | |
1077 | void QQuickRangeSlider::focusInEvent(QFocusEvent *event) |
1078 | { |
1079 | Q_D(QQuickRangeSlider); |
1080 | QQuickControl::focusInEvent(event); |
1081 | |
1082 | // The active focus ends up to RangeSlider when using forceActiveFocus() |
1083 | // or QML KeyNavigation. We must forward the focus to one of the handles, |
1084 | // because RangeSlider handles key events for the focused handle. If |
1085 | // neither handle has active focus, RangeSlider doesn't do anything. |
1086 | QQuickItem *handle = nextItemInFocusChain(); |
1087 | // QQuickItem::nextItemInFocusChain() only works as desired with |
1088 | // Qt::TabFocusAllControls. otherwise pick the first handle |
1089 | if (!handle || handle == this) |
1090 | handle = d->first->handle(); |
1091 | if (handle) |
1092 | handle->forceActiveFocus(reason: event->reason()); |
1093 | } |
1094 | |
1095 | void QQuickRangeSlider::keyPressEvent(QKeyEvent *event) |
1096 | { |
1097 | Q_D(QQuickRangeSlider); |
1098 | QQuickControl::keyPressEvent(event); |
1099 | |
1100 | QQuickRangeSliderNode *focusNode = d->first->handle()->hasActiveFocus() |
1101 | ? d->first : (d->second->handle()->hasActiveFocus() ? d->second : nullptr); |
1102 | if (!focusNode) |
1103 | return; |
1104 | |
1105 | const qreal oldValue = focusNode->value(); |
1106 | if (d->orientation == Qt::Horizontal) { |
1107 | if (event->key() == Qt::Key_Left) { |
1108 | focusNode->setPressed(true); |
1109 | if (isMirrored()) |
1110 | focusNode->increase(); |
1111 | else |
1112 | focusNode->decrease(); |
1113 | event->accept(); |
1114 | } else if (event->key() == Qt::Key_Right) { |
1115 | focusNode->setPressed(true); |
1116 | if (isMirrored()) |
1117 | focusNode->decrease(); |
1118 | else |
1119 | focusNode->increase(); |
1120 | event->accept(); |
1121 | } |
1122 | } else { |
1123 | if (event->key() == Qt::Key_Up) { |
1124 | focusNode->setPressed(true); |
1125 | focusNode->increase(); |
1126 | event->accept(); |
1127 | } else if (event->key() == Qt::Key_Down) { |
1128 | focusNode->setPressed(true); |
1129 | focusNode->decrease(); |
1130 | event->accept(); |
1131 | } |
1132 | } |
1133 | if (!qFuzzyCompare(p1: focusNode->value(), p2: oldValue)) |
1134 | emit focusNode->moved(); |
1135 | } |
1136 | |
1137 | void QQuickRangeSlider::hoverEnterEvent(QHoverEvent *event) |
1138 | { |
1139 | Q_D(QQuickRangeSlider); |
1140 | QQuickControl::hoverEnterEvent(event); |
1141 | d->updateHover(pos: event->position()); |
1142 | event->ignore(); |
1143 | } |
1144 | |
1145 | void QQuickRangeSlider::hoverMoveEvent(QHoverEvent *event) |
1146 | { |
1147 | Q_D(QQuickRangeSlider); |
1148 | QQuickControl::hoverMoveEvent(event); |
1149 | d->updateHover(pos: event->position()); |
1150 | event->ignore(); |
1151 | } |
1152 | |
1153 | void QQuickRangeSlider::hoverLeaveEvent(QHoverEvent *event) |
1154 | { |
1155 | Q_D(QQuickRangeSlider); |
1156 | QQuickControl::hoverLeaveEvent(event); |
1157 | d->first->setHovered(false); |
1158 | d->second->setHovered(false); |
1159 | event->ignore(); |
1160 | } |
1161 | |
1162 | void QQuickRangeSlider::keyReleaseEvent(QKeyEvent *event) |
1163 | { |
1164 | Q_D(QQuickRangeSlider); |
1165 | QQuickControl::keyReleaseEvent(event); |
1166 | d->first->setPressed(false); |
1167 | d->second->setPressed(false); |
1168 | } |
1169 | |
1170 | void QQuickRangeSlider::mousePressEvent(QMouseEvent *event) |
1171 | { |
1172 | Q_D(QQuickRangeSlider); |
1173 | QQuickControl::mousePressEvent(event); |
1174 | d->handleMove(point: event->position(), timestamp: event->timestamp()); |
1175 | setKeepMouseGrab(true); |
1176 | } |
1177 | |
1178 | #if QT_CONFIG(quicktemplates2_multitouch) |
1179 | void QQuickRangeSlider::touchEvent(QTouchEvent *event) |
1180 | { |
1181 | Q_D(QQuickRangeSlider); |
1182 | switch (event->type()) { |
1183 | case QEvent::TouchUpdate: |
1184 | for (const QTouchEvent::TouchPoint &point : event->points()) { |
1185 | if (!d->acceptTouch(point)) |
1186 | continue; |
1187 | |
1188 | switch (point.state()) { |
1189 | case QEventPoint::Pressed: |
1190 | d->handlePress(point: point.position(), timestamp: event->timestamp()); |
1191 | break; |
1192 | case QEventPoint::Updated: |
1193 | if (!keepTouchGrab()) { |
1194 | if (d->orientation == Qt::Horizontal) |
1195 | setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().x() - point.pressPosition().x(), axis: Qt::XAxis, tp: &point, startDragThreshold: qRound(d: d->touchDragThreshold))); |
1196 | else |
1197 | setKeepTouchGrab(QQuickWindowPrivate::dragOverThreshold(d: point.position().y() - point.pressPosition().y(), axis: Qt::YAxis, tp: &point, startDragThreshold: qRound(d: d->touchDragThreshold))); |
1198 | } |
1199 | if (keepTouchGrab()) |
1200 | d->handleMove(point: point.position(), timestamp: event->timestamp()); |
1201 | break; |
1202 | case QEventPoint::Released: |
1203 | d->handleRelease(point: point.position(), timestamp: event->timestamp()); |
1204 | break; |
1205 | default: |
1206 | break; |
1207 | } |
1208 | } |
1209 | break; |
1210 | |
1211 | default: |
1212 | QQuickControl::touchEvent(event); |
1213 | break; |
1214 | } |
1215 | } |
1216 | #endif |
1217 | |
1218 | void QQuickRangeSlider::mirrorChange() |
1219 | { |
1220 | Q_D(QQuickRangeSlider); |
1221 | QQuickControl::mirrorChange(); |
1222 | emit d->first->visualPositionChanged(); |
1223 | emit d->second->visualPositionChanged(); |
1224 | } |
1225 | |
1226 | void QQuickRangeSlider::classBegin() |
1227 | { |
1228 | Q_D(QQuickRangeSlider); |
1229 | QQuickControl::classBegin(); |
1230 | |
1231 | QQmlContext *context = qmlContext(this); |
1232 | if (context) { |
1233 | QQmlEngine::setContextForObject(d->first, context); |
1234 | QQmlEngine::setContextForObject(d->second, context); |
1235 | } |
1236 | } |
1237 | |
1238 | void QQuickRangeSlider::componentComplete() |
1239 | { |
1240 | Q_D(QQuickRangeSlider); |
1241 | QQuickRangeSliderNodePrivate *firstPrivate = QQuickRangeSliderNodePrivate::get(node: d->first); |
1242 | QQuickRangeSliderNodePrivate *secondPrivate = QQuickRangeSliderNodePrivate::get(node: d->second); |
1243 | firstPrivate->executeHandle(complete: true); |
1244 | secondPrivate->executeHandle(complete: true); |
1245 | |
1246 | QQuickControl::componentComplete(); |
1247 | |
1248 | if (firstPrivate->isPendingValue || secondPrivate->isPendingValue |
1249 | || !qFuzzyCompare(p1: d->from, p2: defaultFrom) || !qFuzzyCompare(p1: d->to, p2: defaultTo)) { |
1250 | // Properties were set while we were loading. To avoid clamping issues that occur when setting the |
1251 | // values of first and second overriding values set by the user, set them all at once at the end. |
1252 | // Another reason that we must set these values here is that the from and to values might have made the old range invalid. |
1253 | setValues(firstValue: firstPrivate->isPendingValue ? firstPrivate->pendingValue : firstPrivate->value, |
1254 | secondValue: secondPrivate->isPendingValue ? secondPrivate->pendingValue : secondPrivate->value); |
1255 | |
1256 | firstPrivate->pendingValue = 0; |
1257 | firstPrivate->isPendingValue = false; |
1258 | secondPrivate->pendingValue = 0; |
1259 | secondPrivate->isPendingValue = false; |
1260 | } else { |
1261 | // If there was no pending data, we must still update the positions, |
1262 | // as first.setValue()/second.setValue() won't be called as part of default construction. |
1263 | // Don't need to ignore the second position when updating the first position here, |
1264 | // as our default values are guaranteed to be valid. |
1265 | firstPrivate->updatePosition(); |
1266 | secondPrivate->updatePosition(); |
1267 | } |
1268 | } |
1269 | |
1270 | /*! |
1271 | \qmlmethod void QtQuick.Controls::RangeSlider::first.increase() |
1272 | |
1273 | Increases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. |
1274 | |
1275 | \sa first |
1276 | */ |
1277 | |
1278 | /*! |
1279 | \qmlmethod void QtQuick.Controls::RangeSlider::first.decrease() |
1280 | |
1281 | Decreases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. |
1282 | |
1283 | \sa first |
1284 | */ |
1285 | |
1286 | /*! |
1287 | \qmlmethod void QtQuick.Controls::RangeSlider::second.increase() |
1288 | |
1289 | Increases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. |
1290 | |
1291 | \sa second |
1292 | */ |
1293 | |
1294 | /*! |
1295 | \qmlmethod void QtQuick.Controls::RangeSlider::second.decrease() |
1296 | |
1297 | Decreases the value of the handle by stepSize, or \c 0.1 if stepSize is not defined. |
1298 | |
1299 | \sa second |
1300 | */ |
1301 | |
1302 | #if QT_CONFIG(accessibility) |
1303 | QAccessible::Role QQuickRangeSlider::accessibleRole() const |
1304 | { |
1305 | return QAccessible::Slider; |
1306 | } |
1307 | #endif |
1308 | |
1309 | QT_END_NAMESPACE |
1310 | |
1311 | #include "moc_qquickrangeslider_p.cpp" |
1312 | |