SHOW:
|
|
- or go back to the newest paste.
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 "customqquickdial.h" | |
38 | #include "QtQuickTemplates2/private/qquickdeferredexecute_p_p.h" | |
39 | ||
40 | #include <QtCore/qmath.h> | |
41 | #include <QtQuick/private/qquickflickable_p.h> | |
42 | #include <QtQuickTemplates2/private/qquickcontrol_p_p.h> | |
43 | ||
44 | QT_BEGIN_NAMESPACE | |
45 | ||
46 | /*! | |
47 | \qmltype Dial | |
48 | \inherits Control | |
49 | \instantiates CustomQQuickDial | |
50 | \inqmlmodule QtQuick.Controls | |
51 | \since 5.7 | |
52 | \ingroup qtquickcontrols2-input | |
53 | \brief Circular dial that is rotated to set a value. | |
54 | ||
55 | The Dial is similar to a traditional dial knob that is found on devices | |
56 | such as stereos or industrial equipment. It allows the user to specify a | |
57 | value within a range. | |
58 | ||
59 | \image qtquickcontrols2-dial-no-wrap.gif | |
60 | ||
61 | The value of the dial is set with the \l value property. The range is | |
62 | set with the \l from and \l to properties. To enable or disable wrapping, | |
63 | use the \l wrap property. | |
64 | ||
65 | The dial can be manipulated with a keyboard. It supports the following | |
66 | actions: | |
67 | ||
68 | \table | |
69 | \header \li \b {Action} \li \b {Key} | |
70 | \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Left | |
71 | \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Down | |
72 | \row \li Set \l value to \l from \li \c Qt.Key_Home | |
73 | \row \li Increase \l value by \l stepSize \li \c Qt.Key_Right | |
74 | \row \li Increase \l value by \l stepSize \li \c Qt.Key_Up | |
75 | \row \li Set \l value to \l to \li \c Qt.Key_End | |
76 | \endtable | |
77 | ||
78 | \include qquickdial.qdocinc inputMode | |
79 | ||
80 | \sa {Customizing Dial}, {Input Controls} | |
81 | */ | |
82 | ||
83 | /*! | |
84 | \since QtQuick.Controls 2.2 (Qt 5.9) | |
85 | \qmlsignal QtQuick.Controls::Dial::moved() | |
86 | ||
87 | This signal is emitted when the dial has been interactively moved | |
88 | by the user by either touch, mouse, or keys. | |
89 | */ | |
90 | ||
91 | static const qreal startAngleRadians = (M_PI / 2.0); //240 | |
92 | static const qreal startAngle = 0; //-140 | |
93 | static const qreal endAngleRadians = (M_PI * 2.0)/* * (5.0 / 4.0)*/; //300 | |
94 | static const qreal endAngle = 360; //140 | |
95 | ||
96 | class CustomQQuickDialPrivate : public QQuickControlPrivate | |
97 | { | |
98 | Q_DECLARE_PUBLIC(CustomQQuickDial) | |
99 | ||
100 | public: | |
101 | qreal valueAt(qreal position) const; | |
102 | qreal snapPosition(qreal position) const; | |
103 | qreal positionAt(const QPointF &point) const; | |
104 | qreal circularPositionAt(const QPointF &point) const; | |
105 | qreal linearPositionAt(const QPointF &point) const; | |
106 | void setPosition(qreal position); | |
107 | void updatePosition(); | |
108 | bool isLargeChange(const QPointF &eventPos, qreal proposedPosition) const; | |
109 | bool isHorizontalOrVertical() const; | |
110 | ||
111 | void handlePress(const QPointF &point) override; | |
112 | void handleMove(const QPointF &point) override; | |
113 | void handleRelease(const QPointF &point) override; | |
114 | void handleUngrab() override; | |
115 | ||
116 | void cancelHandle(); | |
117 | void executeHandle(bool complete = false); | |
118 | ||
119 | qreal from = 0; | |
120 | qreal to = 1; | |
121 | qreal value = 0; | |
122 | qreal position = 0; | |
123 | qreal angle = startAngle; | |
124 | qreal stepSize = 0; | |
125 | bool pressed = false; | |
126 | QPointF pressPoint; | |
127 | qreal positionBeforePress = 0; | |
128 | CustomQQuickDial::SnapMode snapMode = CustomQQuickDial::NoSnap; | |
129 | CustomQQuickDial::InputMode inputMode = CustomQQuickDial::Circular; | |
130 | bool wrap = false; | |
131 | bool live = true; | |
132 | QQuickDeferredPointer<QQuickItem> handle; | |
133 | }; | |
134 | ||
135 | qreal CustomQQuickDialPrivate::valueAt(qreal position) const | |
136 | { | |
137 | return from + (to - from) * position; | |
138 | } | |
139 | ||
140 | qreal CustomQQuickDialPrivate::snapPosition(qreal position) const | |
141 | { | |
142 | const qreal range = to - from; | |
143 | if (qFuzzyIsNull(range)) | |
144 | return position; | |
145 | ||
146 | const qreal effectiveStep = stepSize / range; | |
147 | if (qFuzzyIsNull(effectiveStep)) | |
148 | return position; | |
149 | ||
150 | if((position + effectiveStep) > to) | |
151 | return from; | |
152 | ||
153 | return qRound(position / effectiveStep) * effectiveStep; | |
154 | } | |
155 | ||
156 | qreal CustomQQuickDialPrivate::positionAt(const QPointF &point) const | |
157 | { | |
158 | return inputMode == CustomQQuickDial::Circular ? circularPositionAt(point) : linearPositionAt(point); | |
159 | } | |
160 | ||
161 | qreal CustomQQuickDialPrivate::circularPositionAt(const QPointF &point) const | |
162 | { | |
163 | qreal yy = height / 2.0 - point.y(); | |
164 | qreal xx = point.x() - width / 2.0; | |
165 | qreal angle = (xx || yy) ? std::atan2(yy, xx) : 0; | |
166 | ||
167 | angle = -angle; | |
168 | angle += M_PI/2; | |
169 | ||
170 | if (angle < 0) | |
171 | angle = angle + M_PI * 2; | |
172 | ||
173 | angle /= 2.0 * M_PI; | |
174 | ||
175 | return angle; | |
176 | } | |
177 | ||
178 | qreal CustomQQuickDialPrivate::linearPositionAt(const QPointF &point) const | |
179 | { | |
180 | // This value determines the range (either horizontal or vertical) | |
181 | // within which the dial can be dragged. | |
182 | // The larger this value is, the further the drag distance | |
183 | // must be to go from a position of e.g. 0.0 to 1.0. | |
184 | qreal dragArea = 0; | |
185 | ||
186 | // The linear input mode uses a "relative" input system, | |
187 | // where the distance from the press point is used to calculate | |
188 | // the change in position. Moving the mouse above the press | |
189 | // point increases the position (when inputMode is Vertical), | |
190 | // and vice versa. This prevents the dial from jumping when clicked. | |
191 | qreal dragDistance = 0; | |
192 | ||
193 | if (inputMode == CustomQQuickDial::Horizontal) { | |
194 | dragArea = width * 2; | |
195 | dragDistance = pressPoint.x() - point.x(); | |
196 | } else { | |
197 | dragArea = height * 2; | |
198 | dragDistance = point.y() - pressPoint.y(); | |
199 | } | |
200 | const qreal normalisedDifference = dragDistance / dragArea; | |
201 | return qBound(0.0, positionBeforePress - normalisedDifference, 1.0); | |
202 | } | |
203 | ||
204 | void CustomQQuickDialPrivate::setPosition(qreal pos) | |
205 | { | |
206 | Q_Q(CustomQQuickDial); | |
207 | pos = qBound<qreal>(0.0, pos, 1.0); | |
208 | if (qFuzzyCompare(position, pos)) | |
209 | return; | |
210 | ||
211 | position = pos; | |
212 | ||
213 | angle = startAngle + position * qAbs(endAngle - startAngle); | |
214 | ||
215 | emit q->positionChanged(); | |
216 | emit q->angleChanged(); | |
217 | } | |
218 | ||
219 | void CustomQQuickDialPrivate::updatePosition() | |
220 | { | |
221 | qreal pos = 0; | |
222 | if (!qFuzzyCompare(from, to)) | |
223 | pos = (value - from) / (to - from); | |
224 | setPosition(pos); | |
225 | } | |
226 | ||
227 | bool CustomQQuickDialPrivate::isLargeChange(const QPointF &eventPos, qreal proposedPosition) const | |
228 | { | |
229 | return qAbs(proposedPosition - position) >= 0.5 && eventPos.y() >= height / 2; | |
230 | } | |
231 | ||
232 | bool CustomQQuickDialPrivate::isHorizontalOrVertical() const | |
233 | { | |
234 | return inputMode == CustomQQuickDial::Horizontal || inputMode == CustomQQuickDial::Vertical; | |
235 | } | |
236 | ||
237 | void CustomQQuickDialPrivate::handlePress(const QPointF &point) | |
238 | { | |
239 | Q_Q(CustomQQuickDial); | |
240 | QQuickControlPrivate::handlePress(point); | |
241 | pressPoint = point; | |
242 | positionBeforePress = position; | |
243 | q->setPressed(true); | |
244 | } | |
245 | ||
246 | void CustomQQuickDialPrivate::handleMove(const QPointF &point) | |
247 | { | |
248 | Q_Q(CustomQQuickDial); | |
249 | QQuickControlPrivate::handleMove(point); | |
250 | const qreal oldPos = position; | |
251 | qreal pos = positionAt(point); | |
252 | if (snapMode == CustomQQuickDial::SnapAlways) | |
253 | pos = snapPosition(pos); | |
254 | ||
255 | if (wrap || (!wrap && (isHorizontalOrVertical() || !isLargeChange(point, pos)))) { | |
256 | if (live) | |
257 | q->setValue(valueAt(pos)); | |
258 | else | |
259 | setPosition(pos); | |
260 | if (!qFuzzyCompare(pos, oldPos)) | |
261 | emit q->moved(); | |
262 | } | |
263 | } | |
264 | ||
265 | void CustomQQuickDialPrivate::handleRelease(const QPointF &point) | |
266 | { | |
267 | Q_Q(CustomQQuickDial); | |
268 | QQuickControlPrivate::handleRelease(point); | |
269 | if (q->keepMouseGrab() || q->keepTouchGrab()) { | |
270 | const qreal oldPos = position; | |
271 | qreal pos = positionAt(point); | |
272 | if (snapMode != CustomQQuickDial::NoSnap) | |
273 | pos = snapPosition(pos); | |
274 | ||
275 | if (wrap || (!wrap && (isHorizontalOrVertical() || !isLargeChange(point, pos)))) | |
276 | q->setValue(valueAt(pos)); | |
277 | if (!qFuzzyCompare(pos, oldPos)) | |
278 | emit q->moved(); | |
279 | ||
280 | q->setKeepMouseGrab(false); | |
281 | q->setKeepTouchGrab(false); | |
282 | } | |
283 | ||
284 | q->setPressed(false); | |
285 | pressPoint = QPointF(); | |
286 | positionBeforePress = 0; | |
287 | } | |
288 | ||
289 | void CustomQQuickDialPrivate::handleUngrab() | |
290 | { | |
291 | Q_Q(CustomQQuickDial); | |
292 | QQuickControlPrivate::handleUngrab(); | |
293 | pressPoint = QPointF(); | |
294 | positionBeforePress = 0; | |
295 | q->setPressed(false); | |
296 | } | |
297 | ||
298 | static inline QString handleName() { return QStringLiteral("handle"); } | |
299 | ||
300 | void CustomQQuickDialPrivate::cancelHandle() | |
301 | { | |
302 | Q_Q(CustomQQuickDial); | |
303 | quickCancelDeferred(q, handleName()); | |
304 | } | |
305 | ||
306 | void CustomQQuickDialPrivate::executeHandle(bool complete) | |
307 | { | |
308 | Q_Q(CustomQQuickDial); | |
309 | if (handle.wasExecuted()) | |
310 | return; | |
311 | ||
312 | if (!handle || complete) | |
313 | quickBeginDeferred(q, handleName(), handle); | |
314 | if (complete) | |
315 | quickCompleteDeferred(q, handleName(), handle); | |
316 | } | |
317 | ||
318 | CustomQQuickDial::CustomQQuickDial(QQuickItem *parent) | |
319 | : QQuickControl(*(new CustomQQuickDialPrivate), parent) | |
320 | { | |
321 | setActiveFocusOnTab(true); | |
322 | setAcceptedMouseButtons(Qt::LeftButton); | |
323 | #if QT_CONFIG(cursor) | |
324 | setCursor(Qt::ArrowCursor); | |
325 | #endif | |
326 | } | |
327 | ||
328 | /*! | |
329 | \qmlproperty real QtQuick.Controls::Dial::from | |
330 | ||
331 | This property holds the starting value for the range. The default value is \c 0.0. | |
332 | ||
333 | \sa to, value | |
334 | */ | |
335 | qreal CustomQQuickDial::from() const | |
336 | { | |
337 | Q_D(const CustomQQuickDial); | |
338 | return d->from; | |
339 | } | |
340 | ||
341 | void CustomQQuickDial::setFrom(qreal from) | |
342 | { | |
343 | Q_D(CustomQQuickDial); | |
344 | if (qFuzzyCompare(d->from, from)) | |
345 | return; | |
346 | ||
347 | d->from = from; | |
348 | emit fromChanged(); | |
349 | if (isComponentComplete()) { | |
350 | setValue(d->value); | |
351 | d->updatePosition(); | |
352 | } | |
353 | } | |
354 | ||
355 | /*! | |
356 | \qmlproperty real QtQuick.Controls::Dial::to | |
357 | ||
358 | This property holds the end value for the range. The default value is | |
359 | \c 1.0. | |
360 | ||
361 | \sa from, value | |
362 | */ | |
363 | qreal CustomQQuickDial::to() const | |
364 | { | |
365 | Q_D(const CustomQQuickDial); | |
366 | return d->to; | |
367 | } | |
368 | ||
369 | void CustomQQuickDial::setTo(qreal to) | |
370 | { | |
371 | Q_D(CustomQQuickDial); | |
372 | if (qFuzzyCompare(d->to, to)) | |
373 | return; | |
374 | ||
375 | d->to = to; | |
376 | emit toChanged(); | |
377 | if (isComponentComplete()) { | |
378 | setValue(d->value); | |
379 | d->updatePosition(); | |
380 | } | |
381 | } | |
382 | ||
383 | /*! | |
384 | \qmlproperty real QtQuick.Controls::Dial::value | |
385 | ||
386 | This property holds the value in the range \c from - \c to. The default | |
387 | value is \c 0.0. | |
388 | ||
389 | \sa position, live | |
390 | */ | |
391 | qreal CustomQQuickDial::value() const | |
392 | { | |
393 | Q_D(const CustomQQuickDial); | |
394 | return d->value; | |
395 | } | |
396 | ||
397 | void CustomQQuickDial::setValue(qreal value) | |
398 | { | |
399 | Q_D(CustomQQuickDial); | |
400 | if (isComponentComplete()) | |
401 | value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to); | |
402 | ||
403 | if (qFuzzyCompare(d->value, value)) | |
404 | return; | |
405 | ||
406 | d->value = value; | |
407 | d->updatePosition(); | |
408 | emit valueChanged(); | |
409 | } | |
410 | ||
411 | /*! | |
412 | \qmlproperty real QtQuick.Controls::Dial::position | |
413 | \readonly | |
414 | ||
415 | This property holds the logical position of the handle. | |
416 | ||
417 | The position is expressed as a fraction of the control's angle range (the | |
418 | range within which the handle can be moved) in the range \c {0.0 - 1.0}. | |
419 | ||
420 | \sa value, angle | |
421 | */ | |
422 | qreal CustomQQuickDial::position() const | |
423 | { | |
424 | Q_D(const CustomQQuickDial); | |
425 | return d->position; | |
426 | } | |
427 | ||
428 | /*! | |
429 | \qmlproperty real QtQuick.Controls::Dial::angle | |
430 | \readonly | |
431 | ||
432 | This property holds the angle of the handle. | |
433 | ||
434 | The range is from \c -140 degrees to \c 140 degrees. | |
435 | ||
436 | \sa position | |
437 | */ | |
438 | qreal CustomQQuickDial::angle() const | |
439 | { | |
440 | Q_D(const CustomQQuickDial); | |
441 | return d->angle; | |
442 | } | |
443 | ||
444 | /*! | |
445 | \qmlproperty real QtQuick.Controls::Dial::stepSize | |
446 | ||
447 | This property holds the step size. | |
448 | ||
449 | The step size determines the amount by which the dial's value | |
450 | is increased and decreased when interacted with via the keyboard. | |
451 | For example, a step size of \c 0.2, will result in the dial's | |
452 | value increasing and decreasing in increments of \c 0.2. | |
453 | ||
454 | The step size is only respected for touch and mouse interaction | |
455 | when \l snapMode is set to a value other than \c Dial.NoSnap. | |
456 | ||
457 | The default value is \c 0.0, which results in an effective step | |
458 | size of \c 0.1 for keyboard interaction. | |
459 | ||
460 | \sa snapMode, increase(), decrease() | |
461 | */ | |
462 | qreal CustomQQuickDial::stepSize() const | |
463 | { | |
464 | Q_D(const CustomQQuickDial); | |
465 | return d->stepSize; | |
466 | } | |
467 | ||
468 | void CustomQQuickDial::setStepSize(qreal step) | |
469 | { | |
470 | Q_D(CustomQQuickDial); | |
471 | if (qFuzzyCompare(d->stepSize, step)) | |
472 | return; | |
473 | ||
474 | d->stepSize = step; | |
475 | emit stepSizeChanged(); | |
476 | } | |
477 | ||
478 | /*! | |
479 | \qmlproperty enumeration QtQuick.Controls::Dial::snapMode | |
480 | ||
481 | This property holds the snap mode. | |
482 | ||
483 | The snap mode works with the \l stepSize to allow the handle to snap to | |
484 | certain points along the dial. | |
485 | ||
486 | Possible values: | |
487 | \value Dial.NoSnap The dial does not snap (default). | |
488 | \value Dial.SnapAlways The dial snaps while the handle is dragged. | |
489 | \value Dial.SnapOnRelease The dial does not snap while being dragged, but only after the handle is released. | |
490 | ||
491 | \sa stepSize | |
492 | */ | |
493 | CustomQQuickDial::SnapMode CustomQQuickDial::snapMode() const | |
494 | { | |
495 | Q_D(const CustomQQuickDial); | |
496 | return d->snapMode; | |
497 | } | |
498 | ||
499 | void CustomQQuickDial::setSnapMode(SnapMode mode) | |
500 | { | |
501 | Q_D(CustomQQuickDial); | |
502 | if (d->snapMode == mode) | |
503 | return; | |
504 | ||
505 | d->snapMode = mode; | |
506 | emit snapModeChanged(); | |
507 | } | |
508 | ||
509 | /*! | |
510 | \since QtQuick.Controls 2.5 (Qt 5.12) | |
511 | \qmlproperty enumeration QtQuick.Controls::Dial::inputMode | |
512 | ||
513 | This property holds the input mode. | |
514 | ||
515 | \include qquickdial.qdocinc inputMode | |
516 | ||
517 | The default value is \c Dial.Circular. | |
518 | */ | |
519 | CustomQQuickDial::InputMode CustomQQuickDial::inputMode() const | |
520 | { | |
521 | Q_D(const CustomQQuickDial); | |
522 | return d->inputMode; | |
523 | } | |
524 | ||
525 | void CustomQQuickDial::setInputMode(CustomQQuickDial::InputMode mode) | |
526 | { | |
527 | Q_D(CustomQQuickDial); | |
528 | if (d->inputMode == mode) | |
529 | return; | |
530 | ||
531 | d->inputMode = mode; | |
532 | emit inputModeChanged(); | |
533 | } | |
534 | ||
535 | /*! | |
536 | \qmlproperty bool QtQuick.Controls::Dial::wrap | |
537 | ||
538 | This property holds whether the dial wraps when dragged. | |
539 | ||
540 | For example, when this property is set to \c true, dragging the dial past | |
541 | the \l to position will result in the handle being positioned at the | |
542 | \l from position, and vice versa: | |
543 | ||
544 | \image qtquickcontrols2-dial-wrap.gif | |
545 | ||
546 | When this property is \c false, it's not possible to drag the dial across | |
547 | the from and to values. | |
548 | ||
549 | \image qtquickcontrols2-dial-no-wrap.gif | |
550 | ||
551 | The default value is \c false. | |
552 | */ | |
553 | bool CustomQQuickDial::wrap() const | |
554 | { | |
555 | Q_D(const CustomQQuickDial); | |
556 | return d->wrap; | |
557 | } | |
558 | ||
559 | void CustomQQuickDial::setWrap(bool wrap) | |
560 | { | |
561 | Q_D(CustomQQuickDial); | |
562 | if (d->wrap == wrap) | |
563 | return; | |
564 | ||
565 | d->wrap = wrap; | |
566 | emit wrapChanged(); | |
567 | } | |
568 | ||
569 | /*! | |
570 | \qmlproperty bool QtQuick.Controls::Dial::pressed | |
571 | ||
572 | This property holds whether the dial is pressed. | |
573 | ||
574 | The dial will be pressed when either the mouse is pressed over it, or a key | |
575 | such as \c Qt.Key_Left is held down. If you'd prefer not to have the dial | |
576 | be pressed upon key presses (due to styling reasons, for example), you can | |
577 | use the \l {Keys}{Keys attached property}: | |
578 | ||
579 | \code | |
580 | Dial { | |
581 | Keys.onLeftPressed: {} | |
582 | } | |
583 | \endcode | |
584 | ||
585 | This will result in pressed only being \c true upon mouse presses. | |
586 | */ | |
587 | bool CustomQQuickDial::isPressed() const | |
588 | { | |
589 | Q_D(const CustomQQuickDial); | |
590 | return d->pressed; | |
591 | } | |
592 | ||
593 | void CustomQQuickDial::setPressed(bool pressed) | |
594 | { | |
595 | Q_D(CustomQQuickDial); | |
596 | if (d->pressed == pressed) | |
597 | return; | |
598 | ||
599 | d->pressed = pressed; | |
600 | setAccessibleProperty("pressed", pressed); | |
601 | emit pressedChanged(); | |
602 | } | |
603 | ||
604 | /*! | |
605 | \qmlproperty Item QtQuick.Controls::Dial::handle | |
606 | ||
607 | This property holds the handle of the dial. | |
608 | ||
609 | The handle acts as a visual indicator of the position of the dial. | |
610 | ||
611 | \sa {Customizing Dial} | |
612 | */ | |
613 | QQuickItem *CustomQQuickDial::handle() const | |
614 | { | |
615 | CustomQQuickDialPrivate *d = const_cast<CustomQQuickDialPrivate *>(d_func()); | |
616 | if (!d->handle) | |
617 | d->executeHandle(); | |
618 | return d->handle; | |
619 | } | |
620 | ||
621 | void CustomQQuickDial::setHandle(QQuickItem *handle) | |
622 | { | |
623 | Q_D(CustomQQuickDial); | |
624 | if (handle == d->handle) | |
625 | return; | |
626 | ||
627 | if (!d->handle.isExecuting()) | |
628 | d->cancelHandle(); | |
629 | ||
630 | delete d->handle; | |
631 | d->handle = handle; | |
632 | if (d->handle && !d->handle->parentItem()) | |
633 | d->handle->setParentItem(this); | |
634 | if (!d->handle.isExecuting()) | |
635 | emit handleChanged(); | |
636 | } | |
637 | ||
638 | /*! | |
639 | \since QtQuick.Controls 2.2 (Qt 5.9) | |
640 | \qmlproperty bool QtQuick.Controls::Dial::live | |
641 | ||
642 | This property holds whether the dial provides live updates for the \l value | |
643 | property while the handle is dragged. | |
644 | ||
645 | The default value is \c true. | |
646 | ||
647 | \sa value | |
648 | */ | |
649 | bool CustomQQuickDial::live() const | |
650 | { | |
651 | Q_D(const CustomQQuickDial); | |
652 | return d->live; | |
653 | } | |
654 | ||
655 | void CustomQQuickDial::setLive(bool live) | |
656 | { | |
657 | Q_D(CustomQQuickDial); | |
658 | if (d->live == live) | |
659 | return; | |
660 | ||
661 | d->live = live; | |
662 | emit liveChanged(); | |
663 | } | |
664 | ||
665 | /*! | |
666 | \qmlmethod void QtQuick.Controls::Dial::increase() | |
667 | ||
668 | Increases the value by \l stepSize, or \c 0.1 if stepSize is not defined. | |
669 | ||
670 | \sa stepSize | |
671 | */ | |
672 | void CustomQQuickDial::increase() | |
673 | { | |
674 | Q_D(CustomQQuickDial); | |
675 | qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; | |
676 | setValue(d->value + step); | |
677 | } | |
678 | ||
679 | /*! | |
680 | \qmlmethod void QtQuick.Controls::Dial::decrease() | |
681 | ||
682 | Decreases the value by \l stepSize, or \c 0.1 if stepSize is not defined. | |
683 | ||
684 | \sa stepSize | |
685 | */ | |
686 | void CustomQQuickDial::decrease() | |
687 | { | |
688 | Q_D(CustomQQuickDial); | |
689 | qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; | |
690 | setValue(d->value - step); | |
691 | } | |
692 | ||
693 | void CustomQQuickDial::keyPressEvent(QKeyEvent *event) | |
694 | { | |
695 | Q_D(CustomQQuickDial); | |
696 | const qreal oldValue = d->value; | |
697 | switch (event->key()) { | |
698 | case Qt::Key_Left: | |
699 | case Qt::Key_Down: | |
700 | setPressed(true); | |
701 | if (isMirrored()) | |
702 | increase(); | |
703 | else | |
704 | decrease(); | |
705 | break; | |
706 | ||
707 | case Qt::Key_Right: | |
708 | case Qt::Key_Up: | |
709 | setPressed(true); | |
710 | if (isMirrored()) | |
711 | decrease(); | |
712 | else | |
713 | increase(); | |
714 | break; | |
715 | ||
716 | case Qt::Key_Home: | |
717 | setPressed(true); | |
718 | setValue(isMirrored() ? d->to : d->from); | |
719 | break; | |
720 | ||
721 | case Qt::Key_End: | |
722 | setPressed(true); | |
723 | setValue(isMirrored() ? d->from : d->to); | |
724 | break; | |
725 | ||
726 | default: | |
727 | event->ignore(); | |
728 | QQuickControl::keyPressEvent(event); | |
729 | break; | |
730 | } | |
731 | if (!qFuzzyCompare(d->value, oldValue)) | |
732 | emit moved(); | |
733 | } | |
734 | ||
735 | void CustomQQuickDial::keyReleaseEvent(QKeyEvent *event) | |
736 | { | |
737 | QQuickControl::keyReleaseEvent(event); | |
738 | setPressed(false); | |
739 | } | |
740 | ||
741 | void CustomQQuickDial::mousePressEvent(QMouseEvent *event) | |
742 | { | |
743 | Q_D(CustomQQuickDial); | |
744 | QQuickControl::mousePressEvent(event); | |
745 | d->handleMove(event->localPos()); | |
746 | setKeepMouseGrab(true); | |
747 | } | |
748 | ||
749 | #if QT_CONFIG(quicktemplates2_multitouch) | |
750 | void CustomQQuickDial::touchEvent(QTouchEvent *event) | |
751 | { | |
752 | Q_D(CustomQQuickDial); | |
753 | switch (event->type()) { | |
754 | case QEvent::TouchUpdate: | |
755 | for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { | |
756 | if (!d->acceptTouch(point)) | |
757 | continue; | |
758 | ||
759 | switch (point.state()) { | |
760 | case Qt::TouchPointMoved: | |
761 | if (!keepTouchGrab()) { | |
762 | bool overXDragThreshold = QQuickWindowPrivate::dragOverThreshold(point.pos().x() - d->pressPoint.x(), Qt::XAxis, &point); | |
763 | setKeepTouchGrab(overXDragThreshold); | |
764 | ||
765 | if (!overXDragThreshold) { | |
766 | bool overYDragThreshold = QQuickWindowPrivate::dragOverThreshold(point.pos().y() - d->pressPoint.y(), Qt::YAxis, &point); | |
767 | setKeepTouchGrab(overYDragThreshold); | |
768 | } | |
769 | } | |
770 | if (keepTouchGrab()) | |
771 | d->handleMove(point.pos()); | |
772 | break; | |
773 | ||
774 | default: | |
775 | QQuickControl::touchEvent(event); | |
776 | break; | |
777 | } | |
778 | } | |
779 | break; | |
780 | ||
781 | default: | |
782 | QQuickControl::touchEvent(event); | |
783 | break; | |
784 | } | |
785 | } | |
786 | #endif | |
787 | ||
788 | #if QT_CONFIG(wheelevent) | |
789 | void CustomQQuickDial::wheelEvent(QWheelEvent *event) | |
790 | { | |
791 | Q_D(CustomQQuickDial); | |
792 | QQuickControl::wheelEvent(event); | |
793 | if (d->wheelEnabled) { | |
794 | const qreal oldValue = d->value; | |
795 | const QPointF angle = event->angleDelta(); | |
796 | const qreal delta = (qFuzzyIsNull(angle.y()) ? angle.x() : (event->inverted() ? -angle.y() : angle.y())) / QWheelEvent::DefaultDeltasPerStep; | |
797 | const qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize; | |
798 | setValue(oldValue + step * delta); | |
799 | event->setAccepted(!qFuzzyCompare(d->value, oldValue)); | |
800 | } | |
801 | } | |
802 | #endif | |
803 | ||
804 | void CustomQQuickDial::mirrorChange() | |
805 | { | |
806 | QQuickControl::mirrorChange(); | |
807 | emit angleChanged(); | |
808 | } | |
809 | ||
810 | void CustomQQuickDial::componentComplete() | |
811 | { | |
812 | Q_D(CustomQQuickDial); | |
813 | d->executeHandle(true); | |
814 | QQuickControl::componentComplete(); | |
815 | setValue(d->value); | |
816 | d->updatePosition(); | |
817 | } | |
818 | ||
819 | #if QT_CONFIG(accessibility) | |
820 | void CustomQQuickDial::accessibilityActiveChanged(bool active) | |
821 | { | |
822 | QQuickControl::accessibilityActiveChanged(active); | |
823 | ||
824 | Q_D(CustomQQuickDial); | |
825 | if (active) | |
826 | setAccessibleProperty("pressed", d->pressed); | |
827 | } | |
828 | ||
829 | QAccessible::Role CustomQQuickDial::accessibleRole() const | |
830 | { | |
831 | return QAccessible::Dial; | |
832 | } | |
833 | #endif | |
834 | ||
835 | QT_END_NAMESPACE |