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