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