Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickdial.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#include "qquickdial_p.h"
7
8#include <QtCore/qmath.h>
9#include <QtQuick/private/qquickflickable_p.h>
10#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
11#include <QtQuickTemplates2/private/qtquicktemplates2math_p.h>
12
13#include <cmath>
14
15QT_BEGIN_NAMESPACE
16
17/*!
18 \qmltype Dial
19 \inherits Control
20//! \nativetype QQuickDial
21 \inqmlmodule QtQuick.Controls
22 \since 5.7
23 \ingroup qtquickcontrols-input
24 \brief Circular dial that is rotated to set a value.
25
26 The Dial is similar to a traditional dial knob that is found on devices
27 such as stereos or industrial equipment. It allows the user to specify a
28 value within a range.
29
30 \image qtquickcontrols-dial-no-wrap.gif
31
32 The value of the dial is set with the \l value property. The range is
33 set with the \l from and \l to properties. To enable or disable wrapping,
34 use the \l wrap property.
35
36 The dial can be manipulated with a keyboard. It supports the following
37 actions:
38
39 \table
40 \header \li \b {Action} \li \b {Key}
41 \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Left
42 \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Down
43 \row \li Set \l value to \l from \li \c Qt.Key_Home
44 \row \li Increase \l value by \l stepSize \li \c Qt.Key_Right
45 \row \li Increase \l value by \l stepSize \li \c Qt.Key_Up
46 \row \li Set \l value to \l to \li \c Qt.Key_End
47 \endtable
48
49 \include qquickdial.qdocinc inputMode
50
51 \sa {Customizing Dial}, {Input Controls}
52*/
53
54/*!
55 \since QtQuick.Controls 2.2 (Qt 5.9)
56 \qmlsignal QtQuick.Controls::Dial::moved()
57
58 This signal is emitted when the dial has been interactively moved
59 by the user by either touch, mouse, or keys.
60*/
61
62/*!
63 \qmlsignal QtQuick.Controls::Dial::wrapped(Dial.WrapDirection direction)
64 \since 6.6
65
66 This signal is emitted when the dial wraps around, i.e. goes beyond its
67 maximum value to its minimum value, or vice versa. It is only emitted when
68 \l wrap is \c true.
69 The \a direction argument specifies the direction of the full rotation and
70 will be one of the following arguments:
71
72 \value Dial.Clockwise The dial wrapped in clockwise direction.
73 \value Dial.CounterClockwise The dial wrapped in counterclockwise direction.
74*/
75
76// The user angle is the clockwise angle between the position and the vertical
77// y-axis (12 o clock position).
78// Using radians for logic (atan2(...)) and degree for user interface
79constexpr qreal toUserAngleDeg(qreal logicAngleRad) {
80 // minus to turn clockwise, add 90 deg clockwise
81 return -logicAngleRad / M_PI * 180. + 90;
82}
83
84static const qreal defaultStartAngle = -140;
85static const qreal defaultEndAngle = 140;
86
88{
89 Q_DECLARE_PUBLIC(QQuickDial)
90
91public:
93 qreal snapPosition(qreal position) const;
94 qreal positionAt(const QPointF &point) const;
95 qreal circularPositionAt(const QPointF &point) const;
96 qreal linearPositionAt(const QPointF &point) const;
97 void setPosition(qreal position);
99 bool isLargeChange(qreal proposedPosition) const;
101
102 bool handlePress(const QPointF &point, ulong timestamp) override;
103 bool handleMove(const QPointF &point, ulong timestamp) override;
104 bool handleRelease(const QPointF &point, ulong timestamp) override;
106
108 void executeHandle(bool complete = false);
109
111
112 void maybeEmitWrapAround(qreal pos);
113
127 bool wrap = false;
128 bool live = true;
129 bool pressed = false;
131};
132
133qreal QQuickDialPrivate::valueAt(qreal position) const
134{
135 qreal value = from + (to - from) * position;
136
137 /* play nice with users expecting that integer from, to and stepSize leads to
138 integer values - given that we are using floating point internally (and in
139 the API of value), this does not hold, but it is easy enough to handle
140 */
141 if (allValuesAreInteger)
142 value = qRound(value);
143
144 return value;
145}
146
148{
149 const qreal range = to - from;
150 if (qFuzzyIsNull(range))
151 return position;
152
153 const qreal effectiveStep = stepSize / range;
154 if (qFuzzyIsNull(effectiveStep))
155 return position;
156
157 return qRound(position / effectiveStep) * effectiveStep;
158}
159
160qreal QQuickDialPrivate::positionAt(const QPointF &point) const
161{
162 return inputMode == QQuickDial::Circular ? circularPositionAt(point) : linearPositionAt(point);
163}
164
165qreal QQuickDialPrivate::circularPositionAt(const QPointF &point) const
166{
167 qreal yy = height / 2.0 - point.y();
168 qreal xx = point.x() - width / 2.0;
169 qreal alpha = (xx || yy) ? toUserAngleDeg(std::atan2(yy, xx)) : 0;
170
171 // Move around the circle to reach the interval.
172 if (alpha < startAngle && alpha + 360. < endAngle)
173 alpha += 360.;
174 else if (alpha >= endAngle && alpha - 360. >= startAngle)
175 alpha -= 360.;
176
177 // If wrap is on and we are out of the interval [startAngle, endAngle],
178 // we want to jump to the closest border to make it feel nice and responsive
179 if ((alpha < startAngle || alpha > endAngle) && wrap) {
180 if (abs(alpha - startAngle) > abs(endAngle - alpha - 360.))
181 alpha += 360.;
182 else if (abs(alpha - startAngle - 360.) < abs(endAngle - alpha))
183 alpha -= 360.;
184 }
185
186 // If wrap is off,
187 // we want to stay as close as possible to the current angle.
188 // This is important to allow easy setting of boundary values (0,1)
189 if (!wrap) {
190 if (abs(angle - alpha) > abs(angle - (alpha + 360.)))
191 alpha += 360.;
192 if (abs(angle - alpha) > abs(angle - (alpha - 360.)))
193 alpha -= 360.;
194 }
195
196 return (alpha - startAngle) / (endAngle - startAngle);
197}
198
199qreal QQuickDialPrivate::linearPositionAt(const QPointF &point) const
200{
201 // This value determines the range (either horizontal or vertical)
202 // within which the dial can be dragged.
203 // The larger this value is, the further the drag distance
204 // must be to go from a position of e.g. 0.0 to 1.0.
205 qreal dragArea = 0;
206
207 // The linear input mode uses a "relative" input system,
208 // where the distance from the press point is used to calculate
209 // the change in position. Moving the mouse above the press
210 // point increases the position (when inputMode is Vertical),
211 // and vice versa. This prevents the dial from jumping when clicked.
212 qreal dragDistance = 0;
213
214 if (inputMode == QQuickDial::Horizontal) {
215 dragArea = width * 2;
216 dragDistance = pressPoint.x() - point.x();
217 } else {
218 dragArea = height * 2;
219 dragDistance = point.y() - pressPoint.y();
220 }
221 const qreal normalisedDifference = dragDistance / dragArea;
222 return qBound(qreal(0), positionBeforePress - normalisedDifference, qreal(1));
223}
224
226{
227 Q_Q(QQuickDial);
228 pos = qBound<qreal>(qreal(0), pos, qreal(1));
229 const qreal alpha = startAngle + pos * qAbs(endAngle - startAngle);
230 if (qFuzzyCompare(position, pos) && qFuzzyCompare(angle, alpha))
231 return;
232
233 angle = alpha;
234 position = pos;
235
236
237 emit q->positionChanged();
238 emit q->angleChanged();
239}
240
242{
243 qreal pos = 0;
244 if (!qFuzzyCompare(from, to))
245 pos = (value - from) / (to - from);
246 setPosition(pos);
247}
248
249bool QQuickDialPrivate::isLargeChange(qreal proposedPosition) const
250{
251 if (endAngle - startAngle < 180.0)
252 return false;
253 return qAbs(proposedPosition - position) > qreal(0.5);
254}
255
257{
258 return inputMode == QQuickDial::Horizontal || inputMode == QQuickDial::Vertical;
259}
260
261bool QQuickDialPrivate::handlePress(const QPointF &point, ulong timestamp)
262{
263 Q_Q(QQuickDial);
264 QQuickControlPrivate::handlePress(point, timestamp);
265 pressPoint = point;
266 positionBeforePress = position;
267 q->setPressed(true);
268 return true;
269}
270
271bool QQuickDialPrivate::handleMove(const QPointF &point, ulong timestamp)
272{
273 Q_Q(QQuickDial);
274 QQuickControlPrivate::handleMove(point, timestamp);
275 const qreal oldPos = position;
276 qreal pos = qBound(0.0, positionAt(point), 1.0);
277 if (snapMode == QQuickDial::SnapAlways)
278 pos = snapPosition(pos);
279
280 maybeEmitWrapAround(pos);
281
282 if (wrap || isHorizontalOrVertical() || !isLargeChange(pos)) {
283 if (live)
284 q->setValue(valueAt(pos));
285 else
286 setPosition(pos);
287 if (!qFuzzyCompare(pos, oldPos))
288 emit q->moved();
289 }
290 return true;
291}
292
293bool QQuickDialPrivate::handleRelease(const QPointF &point, ulong timestamp)
294{
295 Q_Q(QQuickDial);
296 QQuickControlPrivate::handleRelease(point, timestamp);
297 if (q->keepMouseGrab() || q->keepTouchGrab()) {
298 const qreal oldPos = position;
299 qreal pos = positionAt(point);
300 if (snapMode != QQuickDial::NoSnap)
301 pos = snapPosition(pos);
302
303 maybeEmitWrapAround(pos);
304
305 if (wrap || isHorizontalOrVertical() || !isLargeChange(pos))
306 q->setValue(valueAt(pos));
307 if (!qFuzzyCompare(pos, oldPos))
308 emit q->moved();
309
310 q->setKeepMouseGrab(false);
311 q->setKeepTouchGrab(false);
312 }
313
314 q->setPressed(false);
315 pressPoint = QPointF();
316 positionBeforePress = 0;
317 return true;
318}
319
321{
322 Q_Q(QQuickDial);
323 QQuickControlPrivate::handleUngrab();
324 pressPoint = QPointF();
325 positionBeforePress = 0;
326 q->setPressed(false);
327}
328
330{
331 Q_Q(QQuickDial);
332 quickCancelDeferred(q, handleName());
333}
334
336{
337 Q_Q(QQuickDial);
338 if (handle.wasExecuted())
339 return;
340
341 if (!handle || complete)
342 quickBeginDeferred(q, handleName(), handle);
343 if (complete)
344 quickCompleteDeferred(q, handleName(), handle);
345}
346
348{
349 allValuesAreInteger = areRepresentableAsInteger(to, from, stepSize) && stepSize != 0.0;
350}
351
353{
354 Q_Q(QQuickDial);
355
356 if (wrap && isLargeChange(pos))
357 emit q->wrapped((pos < q->position()) ? QQuickDial::Clockwise : QQuickDial::CounterClockwise);
358}
359
360QQuickDial::QQuickDial(QQuickItem *parent)
361 : QQuickControl(*(new QQuickDialPrivate), parent)
362{
363 setActiveFocusOnTab(true);
364 setAcceptedMouseButtons(Qt::LeftButton);
365#if QT_CONFIG(quicktemplates2_multitouch)
366 setAcceptTouchEvents(true);
367#endif
368#if QT_CONFIG(cursor)
369 setCursor(Qt::ArrowCursor);
370#endif
371 Q_D(QQuickDial);
372 d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
373}
374
375/*!
376 \qmlproperty real QtQuick.Controls::Dial::from
377
378 This property holds the starting value for the range. The default value is \c 0.0.
379
380 \sa to, value
381*/
382qreal QQuickDial::from() const
383{
384 Q_D(const QQuickDial);
385 return d->from;
386}
387
388void QQuickDial::setFrom(qreal from)
389{
390 Q_D(QQuickDial);
391 if (qFuzzyCompare(d->from, from))
392 return;
393
394 d->from = from;
395 emit fromChanged();
396 d->updateAllValuesAreInteger();
397 if (isComponentComplete()) {
398 setValue(d->value);
399 d->updatePosition();
400 }
401}
402
403/*!
404 \qmlproperty real QtQuick.Controls::Dial::to
405
406 This property holds the end value for the range. The default value is
407 \c 1.0.
408
409 \sa from, value
410*/
411qreal QQuickDial::to() const
412{
413 Q_D(const QQuickDial);
414 return d->to;
415}
416
417void QQuickDial::setTo(qreal to)
418{
419 Q_D(QQuickDial);
420 if (qFuzzyCompare(d->to, to))
421 return;
422
423 d->to = to;
424 d->updateAllValuesAreInteger();
425 emit toChanged();
426 if (isComponentComplete()) {
427 setValue(d->value);
428 d->updatePosition();
429 }
430}
431
432/*!
433 \qmlproperty real QtQuick.Controls::Dial::value
434
435 This property holds the value in the range \c from - \c to. The default
436 value is \c 0.0.
437
438 \sa position, live
439*/
440qreal QQuickDial::value() const
441{
442 Q_D(const QQuickDial);
443 return d->value;
444}
445
446void QQuickDial::setValue(qreal value)
447{
448 Q_D(QQuickDial);
449 if (isComponentComplete())
450 value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to);
451
452 if (qFuzzyCompare(d->value, value))
453 return;
454
455 d->value = value;
456 d->updatePosition();
457 emit valueChanged();
458}
459
460/*!
461 \qmlproperty real QtQuick.Controls::Dial::position
462 \readonly
463
464 This property holds the logical position of the handle.
465
466 The position is expressed as a fraction of the control's angle range (the
467 range within which the handle can be moved) in the range \c {0.0 - 1.0}.
468
469 \sa value, angle
470*/
471qreal QQuickDial::position() const
472{
473 Q_D(const QQuickDial);
474 return d->position;
475}
476
477/*!
478 \qmlproperty real QtQuick.Controls::Dial::angle
479 \readonly
480
481 This property holds the clockwise angle of the handle in degrees.
482
483 The angle is zero at the 12 o'clock position and the range is from
484 \l startAngle to \c endAngle.
485
486 \sa position, startAngle, endAngle
487*/
488qreal QQuickDial::angle() const
489{
490 Q_D(const QQuickDial);
491 return d->angle;
492}
493
494/*!
495 \qmlproperty real QtQuick.Controls::Dial::stepSize
496
497 This property holds the step size.
498
499 The step size determines the amount by which the dial's value
500 is increased and decreased when interacted with via the keyboard.
501 For example, a step size of \c 0.2, will result in the dial's
502 value increasing and decreasing in increments of \c 0.2.
503
504 The step size is only respected for touch and mouse interaction
505 when \l snapMode is set to a value other than \c Dial.NoSnap.
506
507 The default value is \c 0.0, which results in an effective step
508 size of \c 0.1 for keyboard interaction.
509
510 \sa snapMode, increase(), decrease()
511*/
512qreal QQuickDial::stepSize() const
513{
514 Q_D(const QQuickDial);
515 return d->stepSize;
516}
517
518void QQuickDial::setStepSize(qreal step)
519{
520 Q_D(QQuickDial);
521 if (qFuzzyCompare(d->stepSize, step))
522 return;
523
524 d->stepSize = step;
525 d->updateAllValuesAreInteger();
526 emit stepSizeChanged();
527}
528
529
530/*!
531 \qmlproperty real QtQuick.Controls::Dial::startAngle
532 \since 6.6
533
534 This property holds the starting angle of the dial in degrees.
535
536 This is the \l angle the dial will have for its minimum value, i.e. \l from.
537 The \l startAngle has to be smaller than the \l endAngle, larger than -360
538 and larger or equal to the \l endAngle - 360 degrees.
539
540 \sa endAngle, angle
541*/
542qreal QQuickDial::startAngle() const
543{
544 Q_D(const QQuickDial);
545 return d->startAngle;
546}
547
548void QQuickDial::setStartAngle(qreal startAngle)
549{
550 Q_D(QQuickDial);
551 if (!d->componentComplete) {
552 // Binding evaluation order can cause warnings with certain combinations
553 // of start and end angles, so delay the actual setting until after component completion.
554 // Store the requested value in the existing member to avoid the need for an extra one.
555 d->startAngle = startAngle;
556 return;
557 }
558
559 if (qFuzzyCompare(d->startAngle, startAngle))
560 return;
561
562 // do not allow to change direction
563 if (startAngle >= d->endAngle) {
564 qmlWarning(this) << "startAngle (" << startAngle
565 << ") cannot be greater than or equal to endAngle (" << d->endAngle << ")";
566 return;
567 }
568
569 // Keep the interval around 0
570 if (startAngle <= -360.) {
571 qmlWarning(this) << "startAngle (" << startAngle << ") cannot be less than or equal to -360";
572 return;
573 }
574
575 // keep the interval [startAngle, endAngle] unique
576 if (startAngle < d->endAngle - 360.) {
577 qmlWarning(this) << "Difference between startAngle (" << startAngle
578 << ") and endAngle (" << d->endAngle << ") cannot be greater than 360."
579 << " Changing endAngle to avoid overlaps.";
580 d->endAngle = startAngle + 360.;
581 emit endAngleChanged();
582 }
583
584 d->startAngle = startAngle;
585 // changing the startAngle will change the angle
586 // if the value is kept constant
587 d->updatePosition();
588 emit startAngleChanged();
589}
590
591/*!
592 \qmlproperty real QtQuick.Controls::Dial::endAngle
593 \since 6.6
594
595 This property holds the end angle of the dial in degrees.
596
597 This is the \l angle the dial will have for its maximum value, i.e. \l to.
598 The \l endAngle has to be bigger than the \l startAngle, smaller than 720
599 and smaller or equal than the \l startAngle + 360 degrees.
600
601 \sa startAngle, angle
602*/
603qreal QQuickDial::endAngle() const
604{
605 Q_D(const QQuickDial);
606 return d->endAngle;
607
608}
609
610void QQuickDial::setEndAngle(qreal endAngle)
611{
612 Q_D(QQuickDial);
613 if (!d->componentComplete) {
614 // Binding evaluation order can cause warnings with certain combinations
615 // of start and end angles, so delay the actual setting until after component completion.
616 // Store the requested value in the existing member to avoid the need for an extra one.
617 d->endAngle = endAngle;
618 return;
619 }
620
621 if (qFuzzyCompare(d->endAngle, endAngle))
622 return;
623
624 if (endAngle <= d->startAngle) {
625 qmlWarning(this) << "endAngle (" << endAngle
626 << ") cannot be less than or equal to startAngle (" << d->startAngle << ")";
627 return;
628 }
629
630 // Keep the interval around 0
631 if (endAngle >= 720.) {
632 qmlWarning(this) << "endAngle (" << endAngle << ") cannot be greater than or equal to 720";
633 return;
634 }
635
636 // keep the interval [startAngle, endAngle] unique
637 if (endAngle > d->startAngle + 360.) {
638 qmlWarning(this) << "Difference between startAngle (" << d->startAngle
639 << ") and endAngle (" << endAngle << ") cannot be greater than 360."
640 << " Changing startAngle to avoid overlaps.";
641 d->startAngle = endAngle - 360.;
642 emit startAngleChanged();
643 }
644
645 d->endAngle = endAngle;
646 // changing the startAngle will change the angle
647 // if the value is kept constant
648 d->updatePosition();
649 emit endAngleChanged();
650}
651
652/*!
653 \qmlproperty enumeration QtQuick.Controls::Dial::snapMode
654
655 This property holds the snap mode.
656
657 The snap mode works with the \l stepSize to allow the handle to snap to
658 certain points along the dial.
659
660 Possible values:
661 \value Dial.NoSnap The dial does not snap (default).
662 \value Dial.SnapAlways The dial snaps while the handle is dragged.
663 \value Dial.SnapOnRelease The dial does not snap while being dragged, but only after the handle is released.
664
665 \sa stepSize
666*/
667QQuickDial::SnapMode QQuickDial::snapMode() const
668{
669 Q_D(const QQuickDial);
670 return d->snapMode;
671}
672
673void QQuickDial::setSnapMode(SnapMode mode)
674{
675 Q_D(QQuickDial);
676 if (d->snapMode == mode)
677 return;
678
679 d->snapMode = mode;
680 emit snapModeChanged();
681}
682
683/*!
684 \since QtQuick.Controls 2.5 (Qt 5.12)
685 \qmlproperty enumeration QtQuick.Controls::Dial::inputMode
686
687 This property holds the input mode.
688
689 \include qquickdial.qdocinc inputMode
690
691 The default value is \c Dial.Circular.
692*/
693QQuickDial::InputMode QQuickDial::inputMode() const
694{
695 Q_D(const QQuickDial);
696 return d->inputMode;
697}
698
699void QQuickDial::setInputMode(QQuickDial::InputMode mode)
700{
701 Q_D(QQuickDial);
702 if (d->inputMode == mode)
703 return;
704
705 d->inputMode = mode;
706 emit inputModeChanged();
707}
708
709/*!
710 \qmlproperty bool QtQuick.Controls::Dial::wrap
711
712 This property holds whether the dial wraps when dragged.
713
714 For example, when this property is set to \c true, dragging the dial past
715 the \l to position will result in the handle being positioned at the
716 \l from position, and vice versa:
717
718 \image qtquickcontrols-dial-wrap.gif
719
720 When this property is \c false, it's not possible to drag the dial across
721 the from and to values.
722
723 \image qtquickcontrols-dial-no-wrap.gif
724
725 The default value is \c false.
726*/
727bool QQuickDial::wrap() const
728{
729 Q_D(const QQuickDial);
730 return d->wrap;
731}
732
733void QQuickDial::setWrap(bool wrap)
734{
735 Q_D(QQuickDial);
736 if (d->wrap == wrap)
737 return;
738
739 d->wrap = wrap;
740 emit wrapChanged();
741}
742
743/*!
744 \qmlproperty bool QtQuick.Controls::Dial::pressed
745
746 This property holds whether the dial is pressed.
747
748 The dial will be pressed when either the mouse is pressed over it, or a key
749 such as \c Qt.Key_Left is held down. If you'd prefer not to have the dial
750 be pressed upon key presses (due to styling reasons, for example), you can
751 use the \l {Keys}{Keys attached property}:
752
753 \code
754 Dial {
755 Keys.onLeftPressed: {}
756 }
757 \endcode
758
759 This will result in pressed only being \c true upon mouse presses.
760*/
761bool QQuickDial::isPressed() const
762{
763 Q_D(const QQuickDial);
764 return d->pressed;
765}
766
767void QQuickDial::setPressed(bool pressed)
768{
769 Q_D(QQuickDial);
770 if (d->pressed == pressed)
771 return;
772
773 d->pressed = pressed;
774 setAccessibleProperty("pressed", pressed);
775 emit pressedChanged();
776}
777
778/*!
779 \qmlproperty Item QtQuick.Controls::Dial::handle
780
781 This property holds the handle of the dial.
782
783 The handle acts as a visual indicator of the position of the dial.
784
785 \sa {Customizing Dial}
786*/
787QQuickItem *QQuickDial::handle() const
788{
789 QQuickDialPrivate *d = const_cast<QQuickDialPrivate *>(d_func());
790 if (!d->handle)
791 d->executeHandle();
792 return d->handle;
793}
794
795void QQuickDial::setHandle(QQuickItem *handle)
796{
797 Q_D(QQuickDial);
798 if (handle == d->handle)
799 return;
800
801 QQuickControlPrivate::warnIfCustomizationNotSupported(this, handle, QStringLiteral("handle"));
802
803 if (!d->handle.isExecuting())
804 d->cancelHandle();
805
806 QQuickControlPrivate::hideOldItem(d->handle);
807 d->handle = handle;
808 if (d->handle && !d->handle->parentItem())
809 d->handle->setParentItem(this);
810 if (!d->handle.isExecuting())
811 emit handleChanged();
812}
813
814/*!
815 \since QtQuick.Controls 2.2 (Qt 5.9)
816 \qmlproperty bool QtQuick.Controls::Dial::live
817
818 This property holds whether the dial provides live updates for the \l value
819 property while the handle is dragged.
820
821 The default value is \c true.
822
823 \sa value
824*/
825bool QQuickDial::live() const
826{
827 Q_D(const QQuickDial);
828 return d->live;
829}
830
831void QQuickDial::setLive(bool live)
832{
833 Q_D(QQuickDial);
834 if (d->live == live)
835 return;
836
837 d->live = live;
838 emit liveChanged();
839}
840
841/*!
842 \qmlmethod void QtQuick.Controls::Dial::increase()
843
844 Increases the value by \l stepSize, or \c 0.1 if stepSize is not defined.
845
846 \sa stepSize
847*/
848void QQuickDial::increase()
849{
850 Q_D(QQuickDial);
851 qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
852 setValue(d->value + step);
853}
854
855/*!
856 \qmlmethod void QtQuick.Controls::Dial::decrease()
857
858 Decreases the value by \l stepSize, or \c 0.1 if stepSize is not defined.
859
860 \sa stepSize
861*/
862void QQuickDial::decrease()
863{
864 Q_D(QQuickDial);
865 qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
866 setValue(d->value - step);
867}
868
869void QQuickDial::keyPressEvent(QKeyEvent *event)
870{
871 Q_D(QQuickDial);
872 const qreal oldValue = d->value;
873 switch (event->key()) {
874 case Qt::Key_Left:
875 case Qt::Key_Down:
876 setPressed(true);
877 if (isMirrored())
878 increase();
879 else
880 decrease();
881 break;
882
883 case Qt::Key_Right:
884 case Qt::Key_Up:
885 setPressed(true);
886 if (isMirrored())
887 decrease();
888 else
889 increase();
890 break;
891
892 case Qt::Key_Home:
893 setPressed(true);
894 setValue(isMirrored() ? d->to : d->from);
895 break;
896
897 case Qt::Key_End:
898 setPressed(true);
899 setValue(isMirrored() ? d->from : d->to);
900 break;
901
902 default:
903 event->ignore();
904 QQuickControl::keyPressEvent(event);
905 break;
906 }
907 if (!qFuzzyCompare(d->value, oldValue))
908 emit moved();
909}
910
911void QQuickDial::keyReleaseEvent(QKeyEvent *event)
912{
913 QQuickControl::keyReleaseEvent(event);
914 setPressed(false);
915}
916
917void QQuickDial::mousePressEvent(QMouseEvent *event)
918{
919 Q_D(QQuickDial);
920 QQuickControl::mousePressEvent(event);
921 d->handleMove(event->position(), event->timestamp());
922 setKeepMouseGrab(true);
923}
924
925#if QT_CONFIG(quicktemplates2_multitouch)
926void QQuickDial::touchEvent(QTouchEvent *event)
927{
928 Q_D(QQuickDial);
929 switch (event->type()) {
930 case QEvent::TouchUpdate:
931 for (const QTouchEvent::TouchPoint &point : event->points()) {
932 if (!d->acceptTouch(point))
933 continue;
934
935 switch (point.state()) {
936 case QEventPoint::Updated:
937 if (!keepTouchGrab()) {
938 bool overXDragThreshold = QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().x() - d->pressPoint.x(),
939 Qt::XAxis, point);
940 setKeepTouchGrab(overXDragThreshold);
941
942 if (!overXDragThreshold) {
943 bool overYDragThreshold = QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().y() - d->pressPoint.y(),
944 Qt::YAxis, point);
945 setKeepTouchGrab(overYDragThreshold);
946 }
947 }
948 if (keepTouchGrab())
949 d->handleMove(point.position(), event->timestamp());
950 break;
951
952 default:
953 QQuickControl::touchEvent(event);
954 break;
955 }
956 }
957 break;
958
959 default:
960 QQuickControl::touchEvent(event);
961 break;
962 }
963}
964#endif
965
966#if QT_CONFIG(wheelevent)
967void QQuickDial::wheelEvent(QWheelEvent *event)
968{
969 Q_D(QQuickDial);
970 QQuickControl::wheelEvent(event);
971 if (d->wheelEnabled) {
972 const qreal oldValue = d->value;
973 const QPointF angle = event->angleDelta();
974 const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : (event->inverted() ? -angle.y() : angle.y())) / int(QWheelEvent::DefaultDeltasPerStep);
975 const qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
976 setValue(oldValue + step * delta);
977 event->setAccepted(!qFuzzyCompare(d->value, oldValue));
978 }
979}
980#endif
981
982void QQuickDial::mirrorChange()
983{
984 QQuickControl::mirrorChange();
985 emit angleChanged();
986}
987
988void QQuickDial::componentComplete()
989{
990 Q_D(QQuickDial);
991 d->executeHandle(true);
992 QQuickControl::componentComplete();
993
994 // Set the (delayed) start and end angles, if necessary (see the setters for more info).
995 if (!qFuzzyCompare(d->startAngle, defaultStartAngle)) {
996 const qreal startAngle = d->startAngle;
997 // Temporarily set it to something else so that it sees that it has changed.
998 d->startAngle = defaultStartAngle;
999 setStartAngle(startAngle);
1000 }
1001
1002 if (!qFuzzyCompare(d->endAngle, defaultEndAngle)) {
1003 const qreal endAngle = d->endAngle;
1004 d->endAngle = defaultEndAngle;
1005 setEndAngle(endAngle);
1006 }
1007
1008 setValue(d->value);
1009 d->updatePosition();
1010}
1011
1012#if QT_CONFIG(accessibility)
1013void QQuickDial::accessibilityActiveChanged(bool active)
1014{
1015 QQuickControl::accessibilityActiveChanged(active);
1016
1017 Q_D(QQuickDial);
1018 if (active)
1019 setAccessibleProperty("pressed", d->pressed);
1020}
1021
1022QAccessible::Role QQuickDial::accessibleRole() const
1023{
1024 return QAccessible::Dial;
1025}
1026#endif
1027
1028QT_END_NAMESPACE
1029
1030#include "moc_qquickdial_p.cpp"
void executeHandle(bool complete=false)
bool handlePress(const QPointF &point, ulong timestamp) override
bool handleRelease(const QPointF &point, ulong timestamp) override
void maybeEmitWrapAround(qreal pos)
qreal linearPositionAt(const QPointF &point) const
void handleUngrab() override
qreal snapPosition(qreal position) const
bool isHorizontalOrVertical() const
qreal positionAt(const QPointF &point) const
bool isLargeChange(qreal proposedPosition) const
QQuickDeferredPointer< QQuickItem > handle
void setPosition(qreal position)
qreal circularPositionAt(const QPointF &point) const
void updateAllValuesAreInteger()
bool handleMove(const QPointF &point, ulong timestamp) override
#define M_PI
Definition qmath.h:200
static const qreal defaultEndAngle
static const qreal defaultStartAngle