8#include <QtCore/qmath.h>
9#include <QtQuick/private/qquickflickable_p.h>
10#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
54
55
56
57
58
59
62
63
64
65
66
67
68
69
70
71
72
73
78constexpr qreal toUserAngleDeg(qreal logicAngleRad) {
80 return -logicAngleRad /
M_PI * 180. + 90;
88 Q_DECLARE_PUBLIC(QQuickDial)
102 bool handleMove(
const QPointF &point, ulong timestamp)
override;
134 qreal value = from + (to - from) * position;
137
138
139
140 if (allValuesAreInteger)
141 value = qRound(value);
148 const qreal range = to - from;
149 if (qFuzzyIsNull(range))
152 const qreal effectiveStep = stepSize / range;
153 if (qFuzzyIsNull(effectiveStep))
156 return qRound(position / effectiveStep) * effectiveStep;
161 return inputMode == QQuickDial::Circular ? circularPositionAt(point) : linearPositionAt(point);
166 qreal yy = height / 2.0 - point.y();
167 qreal xx = point.x() - width / 2.0;
168 qreal alpha = (xx || yy) ? toUserAngleDeg(std::atan2(yy, xx)) : 0;
171 if (alpha < startAngle && alpha + 360. < endAngle)
173 else if (alpha >= endAngle && alpha - 360. >= startAngle)
178 if ((alpha < startAngle || alpha > endAngle) && wrap) {
179 if (abs(alpha - startAngle) > abs(endAngle - alpha - 360.))
181 else if (abs(alpha - startAngle - 360.) < abs(endAngle - alpha))
189 if (abs(angle - alpha) > abs(angle - (alpha + 360.)))
191 if (abs(angle - alpha) > abs(angle - (alpha - 360.)))
195 return (alpha - startAngle) / (endAngle - startAngle);
211 qreal dragDistance = 0;
213 if (inputMode == QQuickDial::Horizontal) {
214 dragArea = width * 2;
215 dragDistance = pressPoint.x() - point.x();
217 dragArea = height * 2;
218 dragDistance = point.y() - pressPoint.y();
220 const qreal normalisedDifference = dragDistance / dragArea;
221 return qBound(qreal(0), positionBeforePress - normalisedDifference, qreal(1));
227 pos = qBound<qreal>(qreal(0), pos, qreal(1));
228 const qreal alpha = startAngle + pos * qAbs(endAngle - startAngle);
229 if (qFuzzyCompare(position, pos) && qFuzzyCompare(angle, alpha))
236 emit q->positionChanged();
237 emit q->angleChanged();
243 if (!qFuzzyCompare(from, to))
244 pos = (value - from) / (to - from);
250 if (endAngle - startAngle < 180.0)
252 return qAbs(proposedPosition - position) > qreal(0.5);
257 return inputMode == QQuickDial::Horizontal || inputMode == QQuickDial::Vertical;
263 QQuickControlPrivate::handlePress(point, timestamp);
265 positionBeforePress = position;
273 QQuickControlPrivate::handleMove(point, timestamp);
274 const qreal oldPos = position;
275 qreal pos = qBound(0.0, positionAt(point), 1.0);
276 if (snapMode == QQuickDial::SnapAlways)
277 pos = snapPosition(pos);
279 maybeEmitWrapAround(pos);
281 if (wrap || isHorizontalOrVertical() || !isLargeChange(pos)) {
283 q->setValue(valueAt(pos));
286 if (!qFuzzyCompare(pos, oldPos))
295 QQuickControlPrivate::handleRelease(point, timestamp);
296 if (q->keepMouseGrab() || q->keepTouchGrab()) {
297 const qreal oldPos = position;
298 qreal pos = positionAt(point);
299 if (snapMode != QQuickDial::NoSnap)
300 pos = snapPosition(pos);
302 maybeEmitWrapAround(pos);
304 if (wrap || isHorizontalOrVertical() || !isLargeChange(pos))
305 q->setValue(valueAt(pos));
306 if (!qFuzzyCompare(pos, oldPos))
309 q->setKeepMouseGrab(
false);
310 q->setKeepTouchGrab(
false);
313 q->setPressed(
false);
314 pressPoint = QPointF();
315 positionBeforePress = 0;
322 QQuickControlPrivate::handleUngrab();
323 pressPoint = QPointF();
324 positionBeforePress = 0;
325 q->setPressed(
false);
331 quickCancelDeferred(q, handleName());
337 if (handle.wasExecuted())
340 if (!handle || complete)
341 quickBeginDeferred(q, handleName(), handle);
343 quickCompleteDeferred(q, handleName(), handle);
346template<
typename ...Real>
348 auto check = [](qreal number) ->
bool {
return std::nearbyint(number) == number; };
349 return (... && check(numbers));
354 allValuesAreInteger = areRepresentableAsInteger(to, from, stepSize) && stepSize != 0.0;
361 if (wrap && isLargeChange(pos))
362 emit q->wrapped((pos < q->position()) ? QQuickDial::Clockwise : QQuickDial::CounterClockwise);
365QQuickDial::QQuickDial(QQuickItem *parent)
366 : QQuickControl(*(
new QQuickDialPrivate), parent)
368 setActiveFocusOnTab(
true);
369 setAcceptedMouseButtons(Qt::LeftButton);
370#if QT_CONFIG(quicktemplates2_multitouch)
371 setAcceptTouchEvents(
true);
374 setCursor(Qt::ArrowCursor);
377 d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Preferred);
381
382
383
384
385
386
387qreal QQuickDial::from()
const
389 Q_D(
const QQuickDial);
393void QQuickDial::setFrom(qreal from)
396 if (qFuzzyCompare(d->from, from))
401 d->updateAllValuesAreInteger();
402 if (isComponentComplete()) {
409
410
411
412
413
414
415
416qreal QQuickDial::to()
const
418 Q_D(
const QQuickDial);
422void QQuickDial::setTo(qreal to)
425 if (qFuzzyCompare(d->to, to))
429 d->updateAllValuesAreInteger();
431 if (isComponentComplete()) {
438
439
440
441
442
443
444
445qreal QQuickDial::value()
const
447 Q_D(
const QQuickDial);
451void QQuickDial::setValue(qreal value)
454 if (isComponentComplete())
455 value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to);
457 if (qFuzzyCompare(d->value, value))
466
467
468
469
470
471
472
473
474
475
476qreal QQuickDial::position()
const
478 Q_D(
const QQuickDial);
483
484
485
486
487
488
489
490
491
492
493qreal QQuickDial::angle()
const
495 Q_D(
const QQuickDial);
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517qreal QQuickDial::stepSize()
const
519 Q_D(
const QQuickDial);
523void QQuickDial::setStepSize(qreal step)
526 if (qFuzzyCompare(d->stepSize, step))
530 d->updateAllValuesAreInteger();
531 emit stepSizeChanged();
536
537
538
539
540
541
542
543
544
545
546
547qreal QQuickDial::startAngle()
const
549 Q_D(
const QQuickDial);
550 return d->startAngle;
553void QQuickDial::setStartAngle(qreal startAngle)
556 if (!d->componentComplete) {
560 d->startAngle = startAngle;
564 if (qFuzzyCompare(d->startAngle, startAngle))
568 if (startAngle >= d->endAngle) {
569 qmlWarning(
this) <<
"startAngle (" << startAngle
570 <<
") cannot be greater than or equal to endAngle (" << d->endAngle <<
")";
575 if (startAngle <= -360.) {
576 qmlWarning(
this) <<
"startAngle (" << startAngle <<
") cannot be less than or equal to -360";
581 if (startAngle < d->endAngle - 360.) {
582 qmlWarning(
this) <<
"Difference between startAngle (" << startAngle
583 <<
") and endAngle (" << d->endAngle <<
") cannot be greater than 360."
584 <<
" Changing endAngle to avoid overlaps.";
585 d->endAngle = startAngle + 360.;
586 emit endAngleChanged();
589 d->startAngle = startAngle;
593 emit startAngleChanged();
597
598
599
600
601
602
603
604
605
606
607
608qreal QQuickDial::endAngle()
const
610 Q_D(
const QQuickDial);
615void QQuickDial::setEndAngle(qreal endAngle)
618 if (!d->componentComplete) {
622 d->endAngle = endAngle;
626 if (qFuzzyCompare(d->endAngle, endAngle))
629 if (endAngle <= d->startAngle) {
630 qmlWarning(
this) <<
"endAngle (" << endAngle
631 <<
") cannot be less than or equal to startAngle (" << d->startAngle <<
")";
636 if (endAngle >= 720.) {
637 qmlWarning(
this) <<
"endAngle (" << endAngle <<
") cannot be greater than or equal to 720";
642 if (endAngle > d->startAngle + 360.) {
643 qmlWarning(
this) <<
"Difference between startAngle (" << d->startAngle
644 <<
") and endAngle (" << endAngle <<
") cannot be greater than 360."
645 <<
" Changing startAngle to avoid overlaps.";
646 d->startAngle = endAngle - 360.;
647 emit startAngleChanged();
650 d->endAngle = endAngle;
654 emit endAngleChanged();
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672QQuickDial::SnapMode QQuickDial::snapMode()
const
674 Q_D(
const QQuickDial);
678void QQuickDial::setSnapMode(SnapMode mode)
681 if (d->snapMode == mode)
685 emit snapModeChanged();
689
690
691
692
693
694
695
696
697
698QQuickDial::InputMode QQuickDial::inputMode()
const
700 Q_D(
const QQuickDial);
704void QQuickDial::setInputMode(QQuickDial::InputMode mode)
707 if (d->inputMode == mode)
711 emit inputModeChanged();
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732bool QQuickDial::wrap()
const
734 Q_D(
const QQuickDial);
738void QQuickDial::setWrap(
bool wrap)
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766bool QQuickDial::isPressed()
const
768 Q_D(
const QQuickDial);
772void QQuickDial::setPressed(
bool pressed)
775 if (d->pressed == pressed)
778 d->pressed = pressed;
779 setAccessibleProperty(
"pressed", pressed);
780 emit pressedChanged();
784
785
786
787
788
789
790
791
792QQuickItem *QQuickDial::handle()
const
794 QQuickDialPrivate *d =
const_cast<QQuickDialPrivate *>(d_func());
800void QQuickDial::setHandle(QQuickItem *handle)
803 if (handle == d->handle)
806 QQuickControlPrivate::warnIfCustomizationNotSupported(
this, handle, QStringLiteral(
"handle"));
808 if (!d->handle.isExecuting())
811 QQuickControlPrivate::hideOldItem(d->handle);
813 if (d->handle && !d->handle->parentItem())
814 d->handle->setParentItem(
this);
815 if (!d->handle.isExecuting())
816 emit handleChanged();
820
821
822
823
824
825
826
827
828
829
830bool QQuickDial::live()
const
832 Q_D(
const QQuickDial);
836void QQuickDial::setLive(
bool live)
847
848
849
850
851
852
853void QQuickDial::increase()
856 qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
857 setValue(d->value + step);
861
862
863
864
865
866
867void QQuickDial::decrease()
870 qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
871 setValue(d->value - step);
874void QQuickDial::keyPressEvent(QKeyEvent *event)
877 const qreal oldValue = d->value;
878 switch (event->key()) {
899 setValue(isMirrored() ? d->to : d->from);
904 setValue(isMirrored() ? d->from : d->to);
909 QQuickControl::keyPressEvent(event);
912 if (!qFuzzyCompare(d->value, oldValue))
916void QQuickDial::keyReleaseEvent(QKeyEvent *event)
918 QQuickControl::keyReleaseEvent(event);
922void QQuickDial::mousePressEvent(QMouseEvent *event)
925 QQuickControl::mousePressEvent(event);
926 d->handleMove(event->position(), event->timestamp());
927 setKeepMouseGrab(
true);
930#if QT_CONFIG(quicktemplates2_multitouch)
931void QQuickDial::touchEvent(QTouchEvent *event)
934 switch (event->type()) {
935 case QEvent::TouchUpdate:
936 for (
const QTouchEvent::TouchPoint &point : event->points()) {
937 if (!d->acceptTouch(point))
940 switch (point.state()) {
941 case QEventPoint::Updated:
942 if (!keepTouchGrab()) {
943 bool overXDragThreshold = QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().x() - d->pressPoint.x(),
945 setKeepTouchGrab(overXDragThreshold);
947 if (!overXDragThreshold) {
948 bool overYDragThreshold = QQuickDeliveryAgentPrivate::dragOverThreshold(point.position().y() - d->pressPoint.y(),
950 setKeepTouchGrab(overYDragThreshold);
954 d->handleMove(point.position(), event->timestamp());
958 QQuickControl::touchEvent(event);
965 QQuickControl::touchEvent(event);
971#if QT_CONFIG(wheelevent)
972void QQuickDial::wheelEvent(QWheelEvent *event)
975 QQuickControl::wheelEvent(event);
976 if (d->wheelEnabled) {
977 const qreal oldValue = d->value;
978 const QPointF angle = event->angleDelta();
979 const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : (event->inverted() ? -angle.y() : angle.y())) /
int(QWheelEvent::DefaultDeltasPerStep);
980 const qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
981 setValue(oldValue + step * delta);
982 event->setAccepted(!qFuzzyCompare(d->value, oldValue));
987void QQuickDial::mirrorChange()
989 QQuickControl::mirrorChange();
993void QQuickDial::componentComplete()
996 d->executeHandle(
true);
997 QQuickControl::componentComplete();
1000 if (!qFuzzyCompare(d->startAngle, defaultStartAngle)) {
1001 const qreal startAngle = d->startAngle;
1003 d->startAngle = defaultStartAngle;
1004 setStartAngle(startAngle);
1007 if (!qFuzzyCompare(d->endAngle, defaultEndAngle)) {
1008 const qreal endAngle = d->endAngle;
1009 d->endAngle = defaultEndAngle;
1010 setEndAngle(endAngle);
1014 d->updatePosition();
1017#if QT_CONFIG(accessibility)
1018void QQuickDial::accessibilityActiveChanged(
bool active)
1020 QQuickControl::accessibilityActiveChanged(active);
1024 setAccessibleProperty(
"pressed", d->pressed);
1027QAccessible::Role QQuickDial::accessibleRole()
const
1029 return QAccessible::Dial;
1035#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
qreal positionBeforePress
QQuickDeferredPointer< QQuickItem > handle
void setPosition(qreal position)
qreal circularPositionAt(const QPointF &point) const
void updateAllValuesAreInteger()
bool handleMove(const QPointF &point, ulong timestamp) override
static const qreal defaultEndAngle
static const qreal defaultStartAngle
static bool areRepresentableAsInteger(Real... numbers)