aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@theqtcompany.com>2015-07-10 15:25:03 +0200
committerMitch Curtis <mitch.curtis@theqtcompany.com>2015-07-16 16:01:35 +0000
commit7975760a4fc0a9ba6f05fa405ba07381131c0f79 (patch)
tree02961959c18e2bd9c9a7358f82ee7bd1c8731f84
parent00eec16115770b0822c250237949243816ac2e1c (diff)
Add Dial.
Change-Id: Ie80fac70141ef72bb68f7c9e91827e834dc1fc4a Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
-rw-r--r--src/extras/doc/images/.directory4
-rw-r--r--src/extras/doc/images/qtquickextras2-dial-background.pngbin0 -> 3009 bytes
-rw-r--r--src/extras/doc/images/qtquickextras2-dial-handle.pngbin0 -> 3765 bytes
-rw-r--r--src/extras/doc/qtquickextras2.qdocconf2
-rw-r--r--src/extras/doc/src/qtquickextras2-customize.qdoc57
-rw-r--r--src/extras/extras.pri2
-rw-r--r--src/extras/qquickdial.cpp527
-rw-r--r--src/extras/qquickdial_p.h141
-rw-r--r--src/imports/extras/Dial.qml83
-rw-r--r--src/imports/extras/extras.pro1
-rw-r--r--src/imports/extras/qmldir1
-rw-r--r--src/imports/extras/qtquickextras2plugin.cpp2
-rw-r--r--tests/auto/extras/data/tst_dial.qml281
13 files changed, 1100 insertions, 1 deletions
diff --git a/src/extras/doc/images/.directory b/src/extras/doc/images/.directory
new file mode 100644
index 00000000..7d49c166
--- /dev/null
+++ b/src/extras/doc/images/.directory
@@ -0,0 +1,4 @@
+[Dolphin]
+Timestamp=2015,7,13,18,47,2
+Version=3
+ViewMode=1
diff --git a/src/extras/doc/images/qtquickextras2-dial-background.png b/src/extras/doc/images/qtquickextras2-dial-background.png
new file mode 100644
index 00000000..8b621dbe
--- /dev/null
+++ b/src/extras/doc/images/qtquickextras2-dial-background.png
Binary files differ
diff --git a/src/extras/doc/images/qtquickextras2-dial-handle.png b/src/extras/doc/images/qtquickextras2-dial-handle.png
new file mode 100644
index 00000000..5caa44ab
--- /dev/null
+++ b/src/extras/doc/images/qtquickextras2-dial-handle.png
Binary files differ
diff --git a/src/extras/doc/qtquickextras2.qdocconf b/src/extras/doc/qtquickextras2.qdocconf
index b97288ce..ee2311c7 100644
--- a/src/extras/doc/qtquickextras2.qdocconf
+++ b/src/extras/doc/qtquickextras2.qdocconf
@@ -28,7 +28,7 @@ depends = qtcore qtgui qtdoc qtqml qtquick qtquicklayouts qtquickdialogs qtquick
# Specify the install path under QT_INSTALL_EXAMPLES
# Examples will be installed under quick/extras - 'extras' subdirectory
# is given as part of \example commands
-exampledirs += ../../../examples/quick/extras
+exampledirs += ../../../examples/quick/extras ../../imports/extras
examplesinstallpath = quick/extras2
headerdirs += ../
diff --git a/src/extras/doc/src/qtquickextras2-customize.qdoc b/src/extras/doc/src/qtquickextras2-customize.qdoc
new file mode 100644
index 00000000..2982aedd
--- /dev/null
+++ b/src/extras/doc/src/qtquickextras2-customize.qdoc
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page qtquickextras2-customize.html
+ \title Customizing Qt Quick Extras 2
+ \brief A set of UI controls to create user interfaces in Qt Quick
+
+ Qt Quick Controls consist of a hierarchy (tree) of items. In order to
+ provide a custom look'n'feel, the default implementation of each can be
+ replaced with a custom item. The following snippets present the default
+ implementations of various items. These can be used as a starting point
+ to implement custom look'n'feel.
+
+ \sa {Customizing Qt Quick Controls 2}
+
+ \section1 Customizing Dial
+
+ Dial consists of two visual items: \l {Control::background}{background}
+ and \l {Dial::handle}{handle}.
+
+ \section3 Background
+
+ \image qtquickextras2-dial-background.png
+
+ \snippet Dial.qml background
+
+ \section3 Indicator
+
+ \image qtquickextras2-dial-handle.png
+
+ \snippet Dial.qml handle
+*/
diff --git a/src/extras/extras.pri b/src/extras/extras.pri
index edbc754a..684fd6e5 100644
--- a/src/extras/extras.pri
+++ b/src/extras/extras.pri
@@ -1,11 +1,13 @@
INCLUDEPATH += $$PWD
HEADERS += \
+ $$PWD/qquickdial_p.h \
$$PWD/qquickdrawer_p.h \
$$PWD/qquickswipeview_p.h \
$$PWD/qquicktumbler_p.h
SOURCES += \
+ $$PWD/qquickdial.cpp \
$$PWD/qquickdrawer.cpp \
$$PWD/qquickswipeview.cpp \
$$PWD/qquicktumbler.cpp
diff --git a/src/extras/qquickdial.cpp b/src/extras/qquickdial.cpp
new file mode 100644
index 00000000..fe196659
--- /dev/null
+++ b/src/extras/qquickdial.cpp
@@ -0,0 +1,527 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickdial_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQuickControls/private/qquickcontrol_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype Dial
+ \inherits Control
+ \instantiates QQuickDial
+ \inqmlmodule QtQuick.Extras
+ \ingroup sliders
+ \brief A circular dial that is rotated to set a value.
+
+ The Dial is similar to a traditional dial knob that is found on devices
+ such as stereos or industrial equipment. It allows the user to specify a
+ value within a range.
+
+ The value of the dial is set with the \l value property. The range is
+ set with the \l from and \l to properties.
+
+ The dial can be manipulated with a keyboard. It supports the following
+ actions:
+
+ \table
+ \header \li \b {Action} \li \b {Key}
+ \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Left
+ \row \li Decrease \l value by \l stepSize \li \c Qt.Key_Down
+ \row \li Set \l value to \l from \li \c Qt.Key_Home
+ \row \li Increase \l value by \l stepSize \li \c Qt.Key_Right
+ \row \li Increase \l value by \l stepSize \li \c Qt.Key_Up
+ \row \li Set \l value to \l to \li \c Qt.Key_End
+ \endtable
+
+ \sa {Customizing Dial}
+*/
+
+static const qreal startAngle = -140;
+static const qreal endAngle = 140;
+
+class QQuickDialPrivate : public QQuickControlPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickDial)
+
+public:
+ QQuickDialPrivate() :
+ from(0),
+ to(1),
+ value(0),
+ position(0),
+ angle(startAngle),
+ stepSize(0),
+ pressed(false),
+ snapMode(QQuickDial::NoSnap),
+ handle(Q_NULLPTR)
+ {
+ }
+
+ qreal valueAt(qreal position) const;
+ qreal snapPosition(qreal position) const;
+ qreal positionAt(const QPoint &point) const;
+ void setPosition(qreal position);
+ void updatePosition();
+
+ qreal from;
+ qreal to;
+ qreal value;
+ qreal position;
+ qreal angle;
+ qreal stepSize;
+ bool pressed;
+ QPoint pressPoint;
+ QQuickDial::SnapMode snapMode;
+ QQuickItem *handle;
+};
+
+qreal QQuickDialPrivate::valueAt(qreal position) const
+{
+ return from + (to - from) * position;
+}
+
+qreal QQuickDialPrivate::snapPosition(qreal position) const
+{
+ if (qFuzzyIsNull(stepSize))
+ return position;
+ return qRound(position / stepSize) * stepSize;
+}
+
+qreal QQuickDialPrivate::positionAt(const QPoint &point) const
+{
+ qreal yy = height / 2.0 - point.y();
+ qreal xx = point.x() - width / 2.0;
+ qreal angle = (xx || yy) ? atan2(yy, xx) : 0;
+
+ if (angle < M_PI / -2)
+ angle = angle + M_PI * 2;
+
+ qreal normalizedAngle = (M_PI * 4 / 3 - angle) / (M_PI * 10 / 6);
+ return normalizedAngle;
+}
+
+void QQuickDialPrivate::setPosition(qreal pos)
+{
+ Q_Q(QQuickDial);
+ pos = qBound(0.0, pos, 1.0);
+ if (!qFuzzyCompare(position, pos)) {
+ position = pos;
+
+ angle = startAngle + position * qAbs(endAngle - startAngle);
+
+ emit q->positionChanged();
+ emit q->angleChanged();
+ }
+}
+
+void QQuickDialPrivate::updatePosition()
+{
+ qreal pos = 0;
+ if (!qFuzzyCompare(from, to))
+ pos = (value - from) / (to - from);
+ setPosition(pos);
+}
+
+QQuickDial::QQuickDial(QQuickItem *parent) :
+ QQuickControl(*(new QQuickDialPrivate), parent)
+{
+ setActiveFocusOnTab(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::from
+
+ This property holds the starting value for the range. The default value is \c 0.0.
+
+ \sa to, value
+*/
+qreal QQuickDial::from() const
+{
+ Q_D(const QQuickDial);
+ return d->from;
+}
+
+void QQuickDial::setFrom(qreal from)
+{
+ Q_D(QQuickDial);
+ if (!qFuzzyCompare(d->from, from)) {
+ d->from = from;
+ emit fromChanged();
+ if (isComponentComplete()) {
+ setValue(d->value);
+ d->updatePosition();
+ }
+ }
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::to
+
+ This property holds the end value for the range. The default value is
+ \c 1.0.
+
+ \sa from, value
+*/
+qreal QQuickDial::to() const
+{
+ Q_D(const QQuickDial);
+ return d->to;
+}
+
+void QQuickDial::setTo(qreal to)
+{
+ Q_D(QQuickDial);
+ if (!qFuzzyCompare(d->to, to)) {
+ d->to = to;
+ emit toChanged();
+ if (isComponentComplete()) {
+ setValue(d->value);
+ d->updatePosition();
+ }
+ }
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::value
+
+ This property holds the value in the range \c from - \c to. The default
+ value is \c 0.0.
+
+ Unlike the \l position property, the \c value is not updated while the
+ handle is dragged. The value is updated after the value has been chosen
+ and the dial has been released.
+
+ \sa position
+*/
+qreal QQuickDial::value() const
+{
+ Q_D(const QQuickDial);
+ return d->value;
+}
+
+void QQuickDial::setValue(qreal value)
+{
+ Q_D(QQuickDial);
+ if (isComponentComplete())
+ value = d->from > d->to ? qBound(d->to, value, d->from) : qBound(d->from, value, d->to);
+
+ if (!qFuzzyCompare(d->value, value)) {
+ d->value = value;
+ d->updatePosition();
+ emit valueChanged();
+ }
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::position
+
+ This property holds the logical position of the handle.
+
+ The position is defined as a percentage of the control's angle range (the
+ range within which the handle can be moved) scaled to \c {0.0 - 1.0}.
+ Unlike the \l value property, the \c position is continuously updated while
+ the handle is dragged.
+
+ \sa value, angle
+*/
+qreal QQuickDial::position() const
+{
+ Q_D(const QQuickDial);
+ return d->position;
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::angle \readonly
+
+ This property holds the angle of the handle.
+
+ Like the \l position property, angle is continuously updated while the
+ handle is dragged.
+
+ \sa position
+*/
+qreal QQuickDial::angle() const
+{
+ Q_D(const QQuickDial);
+ return d->angle;
+}
+
+/*!
+ \qmlproperty real QtQuickExtras2::Dial::stepSize
+
+ This property holds the step size. The default value is \c 0.0.
+
+ \sa snapMode, increase(), decrease()
+*/
+qreal QQuickDial::stepSize() const
+{
+ Q_D(const QQuickDial);
+ return d->stepSize;
+}
+
+void QQuickDial::setStepSize(qreal step)
+{
+ Q_D(QQuickDial);
+ if (!qFuzzyCompare(d->stepSize, step)) {
+ d->stepSize = step;
+ emit stepSizeChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuickExtras2::Dial::snapMode
+
+ This property holds the snap mode.
+
+ The snap mode works with the \l stepSize to allow the handle to snap to
+ certain points along the dial.
+
+ Possible values:
+ \list
+ \li \c Dial.NoSnap (default) - The dial does not snap.
+ \li \c Dial.SnapAlways - The dial snaps while the handle is dragged.
+ \li \c Dial.SnapOnRelease - The dial does not snap while being dragged, but only after the handle is released.
+ \endlist
+
+ \sa stepSize
+*/
+QQuickDial::SnapMode QQuickDial::snapMode() const
+{
+ Q_D(const QQuickDial);
+ return d->snapMode;
+}
+
+void QQuickDial::setSnapMode(SnapMode mode)
+{
+ Q_D(QQuickDial);
+ if (d->snapMode != mode) {
+ d->snapMode = mode;
+ emit snapModeChanged();
+ }
+}
+
+/*!
+ \qmlproperty bool QtQuickExtras2::Dial::pressed
+
+ This property holds whether the dial is pressed.
+
+ The dial will be pressed when either the mouse is pressed over it, or a key
+ such as \c Qt.Key_Left is held down. If you'd prefer not to have the dial
+ be pressed upon key presses (due to styling reasons, for example), you can
+ use the \l {Keys}{Keys attached property}:
+
+ \code
+ Dial {
+ Keys.onLeftPressed: {}
+ }
+ \endcode
+
+ This will result in pressed only being \c true upon mouse presses.
+*/
+bool QQuickDial::isPressed() const
+{
+ Q_D(const QQuickDial);
+ return d->pressed;
+}
+
+void QQuickDial::setPressed(bool pressed)
+{
+ Q_D(QQuickDial);
+ if (d->pressed != pressed) {
+ d->pressed = pressed;
+ emit pressedChanged();
+ }
+}
+
+/*!
+ \qmlmethod void QtQuickExtras2::Dial::increase()
+
+ Increases the value by \l stepSize, or \c 0.1 if stepSize is not defined.
+
+ \sa stepSize
+*/
+void QQuickDial::increase()
+{
+ Q_D(QQuickDial);
+ qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
+ setValue(d->value + step);
+}
+
+/*!
+ \qmlmethod void QtQuickExtras2::Dial::decrease()
+
+ Decreases the value by \l stepSize, or \c 0.1 if stepSize is not defined.
+
+ \sa stepSize
+*/
+void QQuickDial::decrease()
+{
+ Q_D(QQuickDial);
+ qreal step = qFuzzyIsNull(d->stepSize) ? 0.1 : d->stepSize;
+ setValue(d->value - step);
+}
+
+/*!
+ \qmlproperty component QtQuickExtras2::Dial::handle
+
+ This property holds the handle of the dial.
+
+ The handle acts as a visual indicator of the position of the dial.
+
+ \sa {Customizing Dial}
+*/
+QQuickItem *QQuickDial::handle() const
+{
+ Q_D(const QQuickDial);
+ return d->handle;
+}
+
+void QQuickDial::setHandle(QQuickItem *handle)
+{
+ Q_D(QQuickDial);
+ if (handle != d->handle) {
+ d->handle = handle;
+ if (d->handle && !d->handle->parentItem())
+ d->handle->setParentItem(this);
+ emit handleChanged();
+ }
+}
+
+void QQuickDial::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickDial);
+ if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Down) {
+ setPressed(true);
+ if (isMirrored())
+ increase();
+ else
+ decrease();
+ } else if (event->key() == Qt::Key_Right || event->key() == Qt::Key_Up) {
+ setPressed(true);
+ if (isMirrored())
+ decrease();
+ else
+ increase();
+ } else if (event->key() == Qt::Key_Home) {
+ setPressed(true);
+ setValue(isMirrored() ? d->to : d->from);
+ } else if (event->key() == Qt::Key_End) {
+ setPressed(true);
+ setValue(isMirrored() ? d->from : d->to);
+ } else {
+ event->ignore();
+ QQuickControl::keyPressEvent(event);
+ }
+}
+
+void QQuickDial::keyReleaseEvent(QKeyEvent *event)
+{
+ QQuickControl::keyReleaseEvent(event);
+ setPressed(false);
+}
+
+void QQuickDial::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QQuickDial);
+ QQuickControl::mousePressEvent(event);
+ d->pressPoint = event->pos();
+ setPressed(true);
+}
+
+void QQuickDial::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QQuickDial);
+ QQuickControl::mouseMoveEvent(event);
+ if (!keepMouseGrab()) {
+ bool overXDragThreshold = QQuickWindowPrivate::dragOverThreshold(event->pos().x() - d->pressPoint.x(), Qt::XAxis, event);
+ setKeepMouseGrab(overXDragThreshold);
+
+ if (!overXDragThreshold) {
+ bool overYDragThreshold = QQuickWindowPrivate::dragOverThreshold(event->pos().y() - d->pressPoint.y(), Qt::YAxis, event);
+ setKeepMouseGrab(overYDragThreshold);
+ }
+ }
+ if (keepMouseGrab()) {
+ qreal pos = d->positionAt(event->pos());
+ if (d->snapMode == SnapAlways)
+ pos = d->snapPosition(pos);
+ d->setPosition(pos);
+ }
+}
+
+void QQuickDial::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QQuickDial);
+ QQuickControl::mouseReleaseEvent(event);
+ d->pressPoint = QPoint();
+ if (keepMouseGrab()) {
+ qreal pos = d->positionAt(event->pos());
+ if (d->snapMode != NoSnap)
+ pos = d->snapPosition(pos);
+ setValue(d->valueAt(pos));
+ setKeepMouseGrab(false);
+ }
+ setPressed(false);
+}
+
+void QQuickDial::mouseUngrabEvent()
+{
+ Q_D(QQuickDial);
+ QQuickControl::mouseUngrabEvent();
+ d->pressPoint = QPoint();
+ setPressed(false);
+}
+
+void QQuickDial::mirrorChange()
+{
+ QQuickControl::mirrorChange();
+ emit angleChanged();
+}
+
+void QQuickDial::componentComplete()
+{
+ Q_D(QQuickDial);
+ QQuickControl::componentComplete();
+ setValue(d->value);
+ d->updatePosition();
+}
+
+QT_END_NAMESPACE
diff --git a/src/extras/qquickdial_p.h b/src/extras/qquickdial_p.h
new file mode 100644
index 00000000..f1588fff
--- /dev/null
+++ b/src/extras/qquickdial_p.h
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKDIAL_H
+#define QQUICKDIAL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qvariant.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuickControls/private/qquickcontrol_p.h>
+#include <QtQuickExtras/private/qtquickextrasglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickDialAttached;
+class QQuickDialPrivate;
+
+class Q_QUICKEXTRAS_EXPORT QQuickDial : public QQuickControl
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal from READ from WRITE setFrom NOTIFY fromChanged FINAL)
+ Q_PROPERTY(qreal to READ to WRITE setTo NOTIFY toChanged FINAL)
+ Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(qreal position READ position NOTIFY positionChanged FINAL)
+ Q_PROPERTY(qreal angle READ angle NOTIFY angleChanged FINAL)
+ Q_PROPERTY(qreal stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL)
+ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL)
+ Q_PROPERTY(QQuickItem *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL)
+
+public:
+ explicit QQuickDial(QQuickItem *parent = Q_NULLPTR);
+
+ qreal from() const;
+ void setFrom(qreal from);
+
+ qreal to() const;
+ void setTo(qreal to);
+
+ qreal value() const;
+ void setValue(qreal value);
+
+ qreal position() const;
+
+ qreal angle() const;
+
+ qreal stepSize() const;
+ void setStepSize(qreal step);
+
+ enum SnapMode {
+ NoSnap,
+ SnapAlways,
+ SnapOnRelease
+ };
+ Q_ENUM(SnapMode)
+
+ SnapMode snapMode() const;
+ void setSnapMode(SnapMode mode);
+
+ bool isPressed() const;
+ void setPressed(bool pressed);
+
+ QQuickItem *handle() const;
+ void setHandle(QQuickItem *handle);
+
+public Q_SLOTS:
+ void increase();
+ void decrease();
+
+Q_SIGNALS:
+ void fromChanged();
+ void toChanged();
+ void valueChanged();
+ void positionChanged();
+ void angleChanged();
+ void stepSizeChanged();
+ void snapModeChanged();
+ void pressedChanged();
+ void handleChanged();
+
+protected:
+ void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
+ void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
+ void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+ void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+ void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+ void mouseUngrabEvent() Q_DECL_OVERRIDE;
+ void mirrorChange() Q_DECL_OVERRIDE;
+ void componentComplete() Q_DECL_OVERRIDE;
+
+private:
+ Q_DISABLE_COPY(QQuickDial)
+ Q_DECLARE_PRIVATE(QQuickDial)
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKDIAL_H
diff --git a/src/imports/extras/Dial.qml b/src/imports/extras/Dial.qml
new file mode 100644
index 00000000..fbf2807f
--- /dev/null
+++ b/src/imports/extras/Dial.qml
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Extras module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+import QtQuick.Extras 2.0
+
+AbstractDial {
+ id: control
+
+ implicitWidth: 100
+ implicitHeight: 100
+
+ Accessible.pressed: pressed
+ Accessible.role: Accessible.Dial
+
+ //! [background]
+ background: Rectangle {
+ color: control.Theme.backgroundColor
+ radius: width / 2
+
+ border.color: control.activeFocus ? control.Theme.focusColor : control.Theme.frameColor
+ }
+ //! [background]
+
+ //! [handle]
+ handle: Rectangle {
+ id: handleItem
+
+ x: background.width / 2 - handle.width / 2
+ y: background.height / 2 - handle.height / 2
+ transform: [
+ Translate {
+ y: -background.height * 0.35
+ },
+ Rotation {
+ angle: control.angle
+ origin.x: handle.width / 2
+ origin.y: handle.height / 2
+ }
+ ]
+ implicitWidth: 20
+ implicitHeight: 20
+ radius: width / 2
+ border.width: control.activeFocus ? 2 : 1
+ border.color: control.activeFocus ? control.Theme.focusColor : control.Theme.frameColor
+ color: control.Theme.baseColor
+ }
+ //! [handle]
+}
diff --git a/src/imports/extras/extras.pro b/src/imports/extras/extras.pro
index 79e9bbfe..5aa6f04e 100644
--- a/src/imports/extras/extras.pro
+++ b/src/imports/extras/extras.pro
@@ -9,6 +9,7 @@ OTHER_FILES += \
qmldir
QML_FILES = \
+ Dial.qml \
Drawer.qml \
SwipeView.qml \
Tumbler.qml
diff --git a/src/imports/extras/qmldir b/src/imports/extras/qmldir
index 4ba7c40b..51c99e31 100644
--- a/src/imports/extras/qmldir
+++ b/src/imports/extras/qmldir
@@ -1,6 +1,7 @@
module QtQuick.Extras
plugin qtquickextras2plugin
classname QtQuickExtras2Plugin
+Dial 2.0 Dial.qml
Drawer 2.0 Drawer.qml
SwipeView 2.0 SwipeView.qml
Tumbler 2.0 Tumbler.qml
diff --git a/src/imports/extras/qtquickextras2plugin.cpp b/src/imports/extras/qtquickextras2plugin.cpp
index 2a930ffd..a17ae159 100644
--- a/src/imports/extras/qtquickextras2plugin.cpp
+++ b/src/imports/extras/qtquickextras2plugin.cpp
@@ -36,6 +36,7 @@
#include <QtQml/qqmlextensionplugin.h>
+#include <QtQuickExtras/private/qquickdial_p.h>
#include <QtQuickExtras/private/qquickdrawer_p.h>
#include <QtQuickExtras/private/qquickswipeview_p.h>
#include <QtQuickExtras/private/qquicktumbler_p.h>
@@ -53,6 +54,7 @@ public:
void QtQuickExtras2Plugin::registerTypes(const char *uri)
{
+ qmlRegisterType<QQuickDial>(uri, 2, 0, "AbstractDial");
qmlRegisterType<QQuickDrawer>(uri, 2, 0, "AbstractDrawer");
qmlRegisterType<QQuickSwipeView>(uri, 2, 0, "AbstractSwipeView");
qmlRegisterType<QQuickTumbler>(uri, 2, 0, "AbstractTumbler");
diff --git a/tests/auto/extras/data/tst_dial.qml b/tests/auto/extras/data/tst_dial.qml
new file mode 100644
index 00000000..0870f6b5
--- /dev/null
+++ b/tests/auto/extras/data/tst_dial.qml
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtTest 1.0
+import QtQuick.Extras 2.0
+
+TestCase {
+ id: testCase
+ width: 200
+ height: 200
+ visible: true
+ when: windowShown
+ name: "Dial"
+
+ Component {
+ id: dialComponent
+ Dial {}
+ }
+
+ property var dial: null
+
+ function init() {
+ dial = dialComponent.createObject(testCase);
+ verify(dial, "Dial: failed to create an instance");
+ }
+
+ function cleanup() {
+ if (dial)
+ dial.destroy();
+ }
+
+ function test_instance() {
+ compare(dial.value, 0.0);
+ compare(dial.from, 0.0);
+ compare(dial.to, 1.0);
+ compare(dial.stepSize, 0.0);
+ verify(dial.activeFocusOnTab);
+ verify(!dial.pressed);
+ }
+
+ function test_value() {
+ compare(dial.value, 0.0);
+ dial.value = 0.5;
+ compare(dial.value, 0.5);
+ dial.value = 1.0;
+ compare(dial.value, 1.0);
+ dial.value = -1.0;
+ compare(dial.value, 0.0);
+ dial.value = 2.0;
+ compare(dial.value, 1.0);
+ }
+
+ function test_range() {
+ dial.from = 0;
+ dial.to = 100;
+ dial.value = 50;
+ compare(dial.from, 0);
+ compare(dial.to, 100);
+ compare(dial.value, 50);
+ compare(dial.position, 0.5);
+
+ dial.value = 1000
+ compare(dial.value, 100);
+ compare(dial.position, 1);
+
+ dial.value = -1
+ compare(dial.value, 0);
+ compare(dial.position, 0);
+
+ dial.from = 25
+ compare(dial.from, 25);
+ compare(dial.value, 25);
+ compare(dial.position, 0);
+
+ dial.to = 75
+ compare(dial.to, 75);
+ compare(dial.value, 25);
+ compare(dial.position, 0);
+
+ dial.value = 50
+ compare(dial.value, 50);
+ compare(dial.position, 0.5);
+ }
+
+ function test_inverted() {
+ dial.destroy();
+ dial = dialComponent.createObject(testCase, { from: 1.0, to: -1.0 });
+ verify(dial, "Dial: failed to create an instance");
+ compare(dial.from, 1.0);
+ compare(dial.to, -1.0);
+ compare(dial.value, 0.0);
+ compare(dial.position, 0.5);
+
+ dial.value = 2.0;
+ compare(dial.value, 1.0);
+ compare(dial.position, 0.0);
+
+ dial.value = -2.0;
+ compare(dial.value, -1.0);
+ compare(dial.position, 1.0);
+
+ dial.value = 0.0;
+ compare(dial.value, 0.0);
+ compare(dial.position, 0.5);
+ }
+
+ SignalSpy {
+ id: pressSpy
+ signalName: "pressedChanged"
+ }
+
+ function test_pressed() {
+ pressSpy.target = dial;
+ verify(pressSpy.valid);
+ verify(!dial.pressed);
+
+ mousePress(dial, dial.width / 2, dial.height / 2);
+ verify(dial.pressed);
+ compare(pressSpy.count, 1);
+
+ mouseRelease(dial, dial.width / 2, dial.height / 2);
+ verify(!dial.pressed);
+ compare(pressSpy.count, 2);
+ }
+
+ SignalSpy {
+ id: valueSpy
+ signalName: "valueChanged"
+ }
+
+ function test_dragging_data() {
+ return [
+ { tag: "default", from: 0, to: 1, leftValue: 0.20, topValue: 0.5, rightValue: 0.8, bottomValue: 1.0 },
+ { tag: "scaled2", from: 0, to: 2, leftValue: 0.4, topValue: 1.0, rightValue: 1.6, bottomValue: 2.0 },
+ { tag: "scaled1", from: -1, to: 0, leftValue: -0.8, topValue: -0.5, rightValue: -0.2, bottomValue: 0.0 }
+ ]
+ }
+
+ function test_dragging(data) {
+ dial.from = data.from;
+ dial.to = data.to;
+
+ valueSpy.target = dial;
+ verify(valueSpy.valid);
+
+ // drag to the left
+ mouseDrag(dial, dial.width / 2, dial.height / 2, -dial.width / 2, 0, Qt.LeftButton);
+ fuzzyCompare(dial.value, data.leftValue, 0.1);
+ verify(valueSpy.count > 0);
+ valueSpy.clear();
+
+ // drag to the top
+ mouseDrag(dial, dial.width / 2, dial.height / 2, 0, -dial.height / 2, Qt.LeftButton);
+ fuzzyCompare(dial.value, data.topValue, 0.1);
+ verify(valueSpy.count > 0);
+ valueSpy.clear();
+
+ // drag to the right
+ mouseDrag(dial, dial.width / 2, dial.height / 2, dial.width / 2, 0, Qt.LeftButton);
+ fuzzyCompare(dial.value, data.rightValue, 0.1);
+ verify(valueSpy.count > 0);
+ valueSpy.clear();
+
+ // drag to the bottom (* 0.6 to ensure we don't go over to the minimum position)
+ mouseDrag(dial, dial.width / 2, dial.height / 2, 10, dial.height / 2, Qt.LeftButton);
+ fuzzyCompare(dial.value, data.bottomValue, 0.1);
+ verify(valueSpy.count > 0);
+ valueSpy.clear();
+ }
+
+ property Component focusTest: Component {
+ FocusScope {
+ signal receivedKeyPress
+
+ Component.onCompleted: forceActiveFocus()
+ anchors.fill: parent
+ Keys.onPressed: receivedKeyPress()
+ }
+ }
+
+ SignalSpy {
+ id: parentEventSpy
+ }
+
+ function test_keyboardNavigation() {
+ var focusScope = focusTest.createObject(testCase);
+ verify(focusScope);
+
+ // Tests that we've accepted events that we're interested in.
+ parentEventSpy.target = focusScope;
+ parentEventSpy.signalName = "receivedKeyPress";
+
+ dial.parent = focusScope;
+ compare(dial.activeFocusOnTab, true);
+ compare(dial.value, 0);
+
+ dial.focus = true;
+ compare(dial.activeFocus, true);
+ dial.stepSize = 0.1;
+
+ keyClick(Qt.Key_Left);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, 0);
+
+
+ var keyPairs = [[Qt.Key_Left, Qt.Key_Right], [Qt.Key_Down, Qt.Key_Up]];
+ for (var keyPairIndex = 0; keyPairIndex < 2; ++keyPairIndex) {
+ for (var i = 1; i <= 10; ++i) {
+ keyClick(keyPairs[keyPairIndex][1]);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.stepSize * i);
+ }
+
+ compare(dial.value, dial.to);
+
+ for (i = 10; i > 0; --i) {
+ keyClick(keyPairs[keyPairIndex][0]);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.stepSize * (i - 1));
+ }
+ }
+
+ compare(dial.value, dial.from);
+
+ keyClick(Qt.Key_Home);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.from);
+
+ keyClick(Qt.Key_End);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.to);
+
+ keyClick(Qt.Key_End);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.to);
+
+ keyClick(Qt.Key_Home);
+ compare(parentEventSpy.count, 0);
+ compare(dial.value, dial.from);
+
+ focusScope.destroy();
+ }
+}