aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@theqtcompany.com>2015-10-10 16:07:28 +0200
committerJ-P Nurmi <jpnurmi@theqtcompany.com>2015-10-22 14:42:16 +0000
commite3bf4f771458590dd003fc710803c1c1babea68e (patch)
tree853c9805e2c21b79d5df479914da921db5725852
parent51458ba5400c1dcaef1bd2101cbf60a8ddc2a0fa (diff)
Re-introduce SpinBox
It came up in discussions at the QtWS that even if we have Tumbler, people still want and expect to have the good old SpinBox control. SpinBox has it pros, such as that it might work better inside a vertical Flickable, and that in multi-field forms it might visually align better with other controls like TextFields. An early mockup of SpinBox was removed in 1c0edf0. A quote from the commit message: SpinBox is a desktop centric control. It won't be provided in Qt Quick Controls 2.0, but maybe later when desktop support is re-considered. Qt Quick Controls 2.0 will focus on embedded and mobile. SpinBox is still available in 1.x. While it is true that SpinBox might not be optimal for touch or mobile, the real reason for the removal was that validating decimal number input is very complicated. Even though locales have well- defined thousand separators and decimal points, users have very different expectations on how strict or relaxed the input validation should be. This change re-introduces a touch-optimized integer-based SpinBox. What makes it more touch friendly than the earlier version is that it has now auto-repeating buttons. Limiting it to integers avoids the decimal number input validation problem. We can introduce a separate DoubleSpinBox later if necessary - just like in QtWidgets. Change-Id: I2819060eb5d1ae6a8c00b0f12be703456085079d Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
-rw-r--r--src/imports/controls/SpinBox.qml142
-rw-r--r--src/imports/controls/controls.pri1
-rw-r--r--src/imports/controls/doc/images/qtlabscontrols-spinbox-background.pngbin0 -> 1053 bytes
-rw-r--r--src/imports/controls/doc/images/qtlabscontrols-spinbox-contentItem.pngbin0 -> 1144 bytes
-rw-r--r--src/imports/controls/doc/images/qtlabscontrols-spinbox-down.pngbin0 -> 1078 bytes
-rw-r--r--src/imports/controls/doc/images/qtlabscontrols-spinbox-up.pngbin0 -> 1130 bytes
-rw-r--r--src/imports/controls/doc/images/qtlabscontrols-spinbox.pngbin0 -> 1146 bytes
-rw-r--r--src/imports/controls/doc/src/qtlabscontrols-customize.qdoc31
-rw-r--r--src/imports/controls/doc/src/qtlabscontrols-index.qdoc2
-rw-r--r--src/imports/controls/qtlabscontrolsplugin.cpp1
-rw-r--r--src/imports/templates/qtlabstemplatesplugin.cpp3
-rw-r--r--src/templates/qquickspinbox.cpp527
-rw-r--r--src/templates/qquickspinbox_p.h145
-rw-r--r--src/templates/templates.pri2
-rw-r--r--tests/auto/accessibility/data/spinbox.qml16
-rw-r--r--tests/auto/accessibility/tst_accessibility.cpp1
-rw-r--r--tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml5
-rw-r--r--tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp33
-rw-r--r--tests/auto/controls/data/tst_spinbox.qml301
-rw-r--r--tests/auto/snippets/data/qtlabscontrols-spinbox-background.qml11
-rw-r--r--tests/auto/snippets/data/qtlabscontrols-spinbox-contentItem.qml11
-rw-r--r--tests/auto/snippets/data/qtlabscontrols-spinbox-down.qml11
-rw-r--r--tests/auto/snippets/data/qtlabscontrols-spinbox-up.qml11
-rw-r--r--tests/auto/snippets/data/qtlabscontrols-spinbox.qml8
-rw-r--r--tests/benchmarks/objectcount/tst_objectcount.cpp4
25 files changed, 1262 insertions, 4 deletions
diff --git a/src/imports/controls/SpinBox.qml b/src/imports/controls/SpinBox.qml
new file mode 100644
index 00000000..3607a701
--- /dev/null
+++ b/src/imports/controls/SpinBox.qml
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Controls 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 Qt.labs.controls 1.0
+import Qt.labs.templates 1.0 as T
+
+T.SpinBox {
+ id: control
+
+ implicitWidth: Math.max(background ? background.implicitWidth : 0,
+ contentItem.implicitWidth + 2 * padding +
+ (up.indicator ? up.indicator.implicitWidth : 0) +
+ (down.indicator ? down.indicator.implicitWidth : 0))
+ implicitHeight: Math.max(contentItem.implicitHeight + topPadding + bottomPadding,
+ background ? background.implicitHeight : 0,
+ up.indicator ? up.indicator.implicitHeight : 0,
+ down.indicator ? down.indicator.implicitHeight : 0)
+
+ padding: 6
+ leftPadding: 6 + (control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0))
+ rightPadding: 6 + (control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0))
+
+ //! [contentItem]
+ contentItem: TextInput {
+ text: Number(control.value).toLocaleString(control.locale, 'f', 0)
+
+ font: control.font
+ color: control.Theme.textColor
+ selectionColor: control.Theme.selectionColor
+ selectedTextColor: control.Theme.selectedTextColor
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+
+ validator: IntValidator {
+ bottom: Math.min(control.from, control.to)
+ top: Math.max(control.from, control.to)
+ }
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ }
+ //! [contentItem]
+
+ //! [up.indicator]
+ up.indicator: Item {
+ implicitWidth: 26
+ height: parent.height
+ x: control.mirrored ? 0 : parent.width - width
+
+ clip: true
+ Rectangle {
+ x: -radius
+ width: parent.width + radius
+ height: parent.height
+ radius: 3
+ color: Qt.tint(Qt.tint(control.Theme.accentColor,
+ control.activeFocus ? control.Theme.focusColor : "transparent"),
+ control.up.pressed ? control.Theme.pressColor : "transparent")
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: parent.width / 3
+ height: 2
+ color: control.Theme.selectedTextColor
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: 2
+ height: parent.height / 3
+ color: control.Theme.selectedTextColor
+ }
+ }
+ //! [up.indicator]
+
+ //! [down.indicator]
+ down.indicator: Item {
+ implicitWidth: 26
+ height: parent.height
+ x: control.mirrored ? parent.width - width : 0
+
+ clip: true
+ Rectangle {
+ width: parent.width + radius
+ height: parent.height
+ radius: 3
+ color: Qt.tint(Qt.tint(control.Theme.accentColor,
+ control.activeFocus ? control.Theme.focusColor : "transparent"),
+ control.down.pressed ? control.Theme.pressColor : "transparent")
+ }
+ Rectangle {
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+ width: parent.width / 3
+ height: 2
+ color: control.Theme.selectedTextColor
+ }
+ }
+ //! [down.indicator]
+
+ //! [background]
+ background: Rectangle {
+ implicitWidth: 80
+ radius: 3
+ border.width: control.activeFocus ? 2 : 1
+ border.color: control.activeFocus ? control.Theme.focusColor : control.Theme.frameColor
+ }
+ //! [background]
+}
diff --git a/src/imports/controls/controls.pri b/src/imports/controls/controls.pri
index 41651a24..53615c08 100644
--- a/src/imports/controls/controls.pri
+++ b/src/imports/controls/controls.pri
@@ -15,6 +15,7 @@ QML_FILES = \
ScrollBar.qml \
ScrollIndicator.qml \
Slider.qml \
+ SpinBox.qml \
StackView.qml \
Switch.qml \
SwipeView.qml \
diff --git a/src/imports/controls/doc/images/qtlabscontrols-spinbox-background.png b/src/imports/controls/doc/images/qtlabscontrols-spinbox-background.png
new file mode 100644
index 00000000..0224e6f1
--- /dev/null
+++ b/src/imports/controls/doc/images/qtlabscontrols-spinbox-background.png
Binary files differ
diff --git a/src/imports/controls/doc/images/qtlabscontrols-spinbox-contentItem.png b/src/imports/controls/doc/images/qtlabscontrols-spinbox-contentItem.png
new file mode 100644
index 00000000..0e39d92f
--- /dev/null
+++ b/src/imports/controls/doc/images/qtlabscontrols-spinbox-contentItem.png
Binary files differ
diff --git a/src/imports/controls/doc/images/qtlabscontrols-spinbox-down.png b/src/imports/controls/doc/images/qtlabscontrols-spinbox-down.png
new file mode 100644
index 00000000..a6bbecdf
--- /dev/null
+++ b/src/imports/controls/doc/images/qtlabscontrols-spinbox-down.png
Binary files differ
diff --git a/src/imports/controls/doc/images/qtlabscontrols-spinbox-up.png b/src/imports/controls/doc/images/qtlabscontrols-spinbox-up.png
new file mode 100644
index 00000000..3c6c870b
--- /dev/null
+++ b/src/imports/controls/doc/images/qtlabscontrols-spinbox-up.png
Binary files differ
diff --git a/src/imports/controls/doc/images/qtlabscontrols-spinbox.png b/src/imports/controls/doc/images/qtlabscontrols-spinbox.png
new file mode 100644
index 00000000..39fa813a
--- /dev/null
+++ b/src/imports/controls/doc/images/qtlabscontrols-spinbox.png
Binary files differ
diff --git a/src/imports/controls/doc/src/qtlabscontrols-customize.qdoc b/src/imports/controls/doc/src/qtlabscontrols-customize.qdoc
index 474caba9..c4124191 100644
--- a/src/imports/controls/doc/src/qtlabscontrols-customize.qdoc
+++ b/src/imports/controls/doc/src/qtlabscontrols-customize.qdoc
@@ -305,6 +305,37 @@
\snippet Slider.qml handle
+ \section1 Customizing SpinBox
+
+ SpinBox consists of four visual items: \l {Control::background}{background},
+ \l {Control::contentItem}{contentItem}, \l {SpinBox::up.indicator}{up indicator},
+ and \l {SpinBox::down.indicator}{down indicator}.
+
+ \section3 Background
+
+ \image qtlabscontrols-spinbox-background.png
+
+ \snippet SpinBox.qml background
+
+ \section3 Content item
+
+ \image qtlabscontrols-spinbox-contentItem.png
+
+ \snippet SpinBox.qml contentItem
+
+ \section3 Down indicator
+
+ \image qtlabscontrols-spinbox-down.png
+
+ \snippet Spinbox.qml down.indicator
+
+ \section3 Up indicator
+
+ \image qtlabscontrols-spinbox-up.png
+
+ \snippet Spinbox.qml up.indicator
+
+
\section1 Customizing StackView
StackView can have a visual \l {Control::background}{background}
diff --git a/src/imports/controls/doc/src/qtlabscontrols-index.qdoc b/src/imports/controls/doc/src/qtlabscontrols-index.qdoc
index 557bc73a..c105ede9 100644
--- a/src/imports/controls/doc/src/qtlabscontrols-index.qdoc
+++ b/src/imports/controls/doc/src/qtlabscontrols-index.qdoc
@@ -250,7 +250,7 @@
\li \l [QtLabsControls] {Slider}
\row
\li \l [QtQuickControls] {SpinBox}
- \li No equivalent; see \l [QtLabsControls] {Tumbler} instead.
+ \li \l [QtLabsControls] {SpinBox}
\row
\li \l [QtQuickControls] {Stack},
\l [QtQuickControls] {StackView},
diff --git a/src/imports/controls/qtlabscontrolsplugin.cpp b/src/imports/controls/qtlabscontrolsplugin.cpp
index 8d042381..249375d2 100644
--- a/src/imports/controls/qtlabscontrolsplugin.cpp
+++ b/src/imports/controls/qtlabscontrolsplugin.cpp
@@ -85,6 +85,7 @@ void QtLabsControlsPlugin::registerTypes(const char *uri)
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/ScrollBar.qml"))), uri, 1, 0, "ScrollBar");
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/ScrollIndicator.qml"))), uri, 1, 0, "ScrollIndicator");
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/Slider.qml"))), uri, 1, 0, "Slider");
+ qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/SpinBox.qml"))), uri, 1, 0, "SpinBox");
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/StackView.qml"))), uri, 1, 0, "StackView");
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/SwipeView.qml"))), uri, 1, 0, "SwipeView");
qmlRegisterType(selector.select(QUrl(base + QStringLiteral("/Switch.qml"))), uri, 1, 0, "Switch");
diff --git a/src/imports/templates/qtlabstemplatesplugin.cpp b/src/imports/templates/qtlabstemplatesplugin.cpp
index 51c15dd9..cf97f743 100644
--- a/src/imports/templates/qtlabstemplatesplugin.cpp
+++ b/src/imports/templates/qtlabstemplatesplugin.cpp
@@ -54,6 +54,7 @@
#include <QtLabsTemplates/private/qquickscrollbar_p.h>
#include <QtLabsTemplates/private/qquickscrollindicator_p.h>
#include <QtLabsTemplates/private/qquickslider_p.h>
+#include <QtLabsTemplates/private/qquickspinbox_p.h>
#include <QtLabsTemplates/private/qquickstackview_p.h>
#include <QtLabsTemplates/private/qquickswipeview_p.h>
#include <QtLabsTemplates/private/qquickswitch_p.h>
@@ -97,6 +98,8 @@ void QtLabsTemplatesPlugin::registerTypes(const char *uri)
qmlRegisterType<QQuickScrollBar>(uri, 1, 0, "ScrollBar");
qmlRegisterType<QQuickScrollIndicator>(uri, 1, 0, "ScrollIndicator");
qmlRegisterType<QQuickSlider>(uri, 1, 0, "Slider");
+ qmlRegisterType<QQuickSpinBox>(uri, 1, 0, "SpinBox");
+ qmlRegisterType<QQuickSpinner>();
qmlRegisterType<QQuickStackView>(uri, 1, 0, "StackView");
qmlRegisterType<QQuickSwipeViewAttached>();
qmlRegisterType<QQuickSwipeView>(uri, 1, 0, "SwipeView");
diff --git a/src/templates/qquickspinbox.cpp b/src/templates/qquickspinbox.cpp
new file mode 100644
index 00000000..cac210ec
--- /dev/null
+++ b/src/templates/qquickspinbox.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 Labs Templates 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 "qquickspinbox_p.h"
+#include "qquickcontrol_p_p.h"
+
+#include <QtGui/qguiapplication.h>
+#include <QtGui/qstylehints.h>
+
+QT_BEGIN_NAMESPACE
+
+// copied from qabstractbutton.cpp
+static const int AUTO_REPEAT_DELAY = 300;
+static const int AUTO_REPEAT_INTERVAL = 100;
+
+/*!
+ \qmltype SpinBox
+ \inherits Control
+ \instantiates QQuickSpinBox
+ \inqmlmodule Qt.labs.controls
+ \ingroup input
+ \brief A spinbox control.
+
+ \image qtlabscontrols-spinbox.png
+
+ SpinBox allows the user to choose an integer value by clicking the up
+ or down indicator buttons, by pressing up or down on the keyboard, or
+ by entering a text value in the input field.
+
+ By default, SpinBox provides discrete values in the range of \c [0-99]
+ with a \l stepSize of \c 1.
+
+ \snippet qtlabscontrols-spinbox.qml 1
+
+ \sa Tumbler, {Customizing SpinBox}
+*/
+
+class QQuickSpinBoxPrivate : public QQuickControlPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSpinBox)
+
+public:
+ QQuickSpinBoxPrivate() : from(0), to(99), value(0), stepSize(1),
+ delayTimer(0), repeatTimer(0), up(Q_NULLPTR), down(Q_NULLPTR) { }
+
+ int boundValue(int value) const;
+ void updateValue();
+
+ int effectiveStepSize() const;
+
+ void startRepeatDelay();
+ void startPressRepeat();
+ void stopPressRepeat();
+
+ bool handleMousePressEvent(QQuickItem *child, QMouseEvent *event);
+ bool handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event);
+ bool handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event);
+ bool handleMouseUngrabEvent(QQuickItem *child);
+
+ int from;
+ int to;
+ int value;
+ int stepSize;
+ int delayTimer;
+ int repeatTimer;
+ QQuickSpinner *up;
+ QQuickSpinner *down;
+ QLocale locale;
+};
+
+int QQuickSpinBoxPrivate::boundValue(int value) const
+{
+ return from > to ? qBound(to, value, from) : qBound(from, value, to);
+}
+
+void QQuickSpinBoxPrivate::updateValue()
+{
+ Q_Q(QQuickSpinBox);
+ if (contentItem) {
+ QVariant text = contentItem->property("text");
+ if (text.isValid())
+ q->setValue(locale.toInt(text.toString()));
+ }
+}
+
+int QQuickSpinBoxPrivate::effectiveStepSize() const
+{
+ return from > to ? -1 * stepSize : stepSize;
+}
+
+void QQuickSpinBoxPrivate::startRepeatDelay()
+{
+ Q_Q(QQuickSpinBox);
+ stopPressRepeat();
+ delayTimer = q->startTimer(AUTO_REPEAT_DELAY);
+}
+
+void QQuickSpinBoxPrivate::startPressRepeat()
+{
+ Q_Q(QQuickSpinBox);
+ stopPressRepeat();
+ repeatTimer = q->startTimer(AUTO_REPEAT_INTERVAL);
+}
+
+void QQuickSpinBoxPrivate::stopPressRepeat()
+{
+ Q_Q(QQuickSpinBox);
+ if (delayTimer > 0) {
+ q->killTimer(delayTimer);
+ delayTimer = 0;
+ }
+ if (repeatTimer > 0) {
+ q->killTimer(repeatTimer);
+ repeatTimer = 0;
+ }
+}
+
+bool QQuickSpinBoxPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent *)
+{
+ Q_Q(QQuickSpinBox);
+ QQuickItem *ui = up->indicator();
+ QQuickItem *di = down->indicator();
+ up->setPressed(child == ui);
+ down->setPressed(child == di);
+
+ bool pressed = up->isPressed() || down->isPressed();
+ q->setAccessibleProperty("pressed", pressed);
+ if (pressed)
+ startRepeatDelay();
+ return child == ui || child == di;
+}
+
+bool QQuickSpinBoxPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event)
+{
+ Q_Q(QQuickSpinBox);
+ QQuickItem *ui = up->indicator();
+ QQuickItem *di = down->indicator();
+ up->setPressed(child == ui && ui->contains(event->pos()));
+ down->setPressed(child == di && di->contains(event->pos()));
+
+ bool pressed = up->isPressed() || down->isPressed();
+ q->setAccessibleProperty("pressed", pressed);
+ stopPressRepeat();
+ return child == ui || child == di;
+}
+
+bool QQuickSpinBoxPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event)
+{
+ Q_Q(QQuickSpinBox);
+ QQuickItem *ui = up->indicator();
+ QQuickItem *di = down->indicator();
+ if (child == ui) {
+ up->setPressed(false);
+ if (repeatTimer <= 0 && ui->contains(event->pos()))
+ q->increase();
+ } else if (child == di) {
+ down->setPressed(false);
+ if (repeatTimer <= 0 && di->contains(event->pos()))
+ q->decrease();
+ }
+
+ q->setAccessibleProperty("pressed", false);
+ stopPressRepeat();
+ return child == ui || child == di;
+}
+
+bool QQuickSpinBoxPrivate::handleMouseUngrabEvent(QQuickItem *child)
+{
+ Q_Q(QQuickSpinBox);
+ QQuickItem *ui = up->indicator();
+ QQuickItem *di = down->indicator();
+ up->setPressed(false);
+ down->setPressed(false);
+
+ q->setAccessibleProperty("pressed", false);
+ stopPressRepeat();
+ return child == ui || child == di;
+}
+
+QQuickSpinBox::QQuickSpinBox(QQuickItem *parent) :
+ QQuickControl(*(new QQuickSpinBoxPrivate), parent)
+{
+ Q_D(QQuickSpinBox);
+ d->up = new QQuickSpinner(this);
+ d->down = new QQuickSpinner(this);
+
+ setFlag(ItemIsFocusScope);
+ setFiltersChildMouseEvents(true);
+ setAccessibleRole(0x00000034); //QAccessible::SpinBox
+}
+
+/*!
+ \qmlproperty int Qt.labs.controls::SpinBox::from
+
+ This property holds the starting value for the range. The default value is \c 0.
+
+ \sa to, value
+*/
+int QQuickSpinBox::from() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->from;
+}
+
+void QQuickSpinBox::setFrom(int from)
+{
+ Q_D(QQuickSpinBox);
+ if (d->from != from) {
+ d->from = from;
+ emit fromChanged();
+ if (isComponentComplete())
+ setValue(d->value);
+ }
+}
+
+/*!
+ \qmlproperty int Qt.labs.controls::SpinBox::to
+
+ This property holds the end value for the range. The default value is \c 99.
+
+ \sa from, value
+*/
+int QQuickSpinBox::to() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->to;
+}
+
+void QQuickSpinBox::setTo(int to)
+{
+ Q_D(QQuickSpinBox);
+ if (d->to != to) {
+ d->to = to;
+ emit toChanged();
+ if (isComponentComplete())
+ setValue(d->value);
+ }
+}
+
+/*!
+ \qmlproperty int Qt.labs.controls::SpinBox::value
+
+ This property holds the value in the range \c from - \c to. The default value is \c 0.
+*/
+int QQuickSpinBox::value() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->value;
+}
+
+void QQuickSpinBox::setValue(int value)
+{
+ Q_D(QQuickSpinBox);
+ if (isComponentComplete())
+ value = d->boundValue(value);
+
+ if (d->value != value) {
+ d->value = value;
+ emit valueChanged();
+ }
+}
+
+/*!
+ \qmlproperty int Qt.labs.controls::SpinBox::stepSize
+
+ This property holds the step size. The default value is \c 1.
+
+ \sa increase(), decrease()
+*/
+int QQuickSpinBox::stepSize() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->stepSize;
+}
+
+void QQuickSpinBox::setStepSize(int step)
+{
+ Q_D(QQuickSpinBox);
+ if (d->stepSize != step) {
+ d->stepSize = step;
+ emit stepSizeChanged();
+ }
+}
+
+/*!
+ \qmlproperty Locale Qt.labs.controls::SpinBox::locale
+
+ This property holds the locale that is used to format the value.
+*/
+QLocale QQuickSpinBox::locale() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->locale;
+}
+
+void QQuickSpinBox::setLocale(const QLocale &locale)
+{
+ Q_D(QQuickSpinBox);
+ if (d->locale != locale) {
+ d->locale = locale;
+ emit localeChanged();
+ }
+}
+
+/*!
+ \qmlpropertygroup Qt.labs.controls::SpinBox::up
+ \qmlproperty bool Qt.labs.controls::SpinBox::up.pressed
+ \qmlproperty Item Qt.labs.controls::SpinBox::up.indicator
+
+ These properties hold the up indicator item and whether it is pressed.
+
+ \sa increase()
+*/
+QQuickSpinner *QQuickSpinBox::up() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->up;
+}
+
+/*!
+ \qmlpropertygroup Qt.labs.controls::SpinBox::down
+ \qmlproperty bool Qt.labs.controls::SpinBox::down.pressed
+ \qmlproperty Item Qt.labs.controls::SpinBox::down.indicator
+
+ These properties hold the down indicator item and whether it is pressed.
+
+ \sa decrease()
+*/
+QQuickSpinner *QQuickSpinBox::down() const
+{
+ Q_D(const QQuickSpinBox);
+ return d->down;
+}
+
+/*!
+ \qmlmethod void Qt.labs.controls::SpinBox::increase()
+
+ Increases the value by \l stepSize.
+
+ \sa stepSize
+*/
+void QQuickSpinBox::increase()
+{
+ Q_D(QQuickSpinBox);
+ setValue(d->value + d->effectiveStepSize());
+}
+
+/*!
+ \qmlmethod void Qt.labs.controls::SpinBox::decrease()
+
+ Decreases the value by \l stepSize.
+
+ \sa stepSize
+*/
+void QQuickSpinBox::decrease()
+{
+ Q_D(QQuickSpinBox);
+ setValue(d->value - d->effectiveStepSize());
+}
+
+void QQuickSpinBox::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QQuickSpinBox);
+ QQuickControl::keyPressEvent(event);
+
+ switch (event->key()) {
+ case Qt::Key_Up:
+ increase();
+ d->up->setPressed(true);
+ event->accept();
+ break;
+
+ case Qt::Key_Down:
+ decrease();
+ d->down->setPressed(true);
+ event->accept();
+ break;
+
+ default:
+ break;
+ }
+
+ setAccessibleProperty("pressed", d->up->isPressed() || d->down->isPressed());
+}
+
+void QQuickSpinBox::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QQuickSpinBox);
+ QQuickControl::keyReleaseEvent(event);
+
+ if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
+ d->updateValue();
+
+ d->up->setPressed(false);
+ d->down->setPressed(false);
+ setAccessibleProperty("pressed", false);
+}
+
+bool QQuickSpinBox::childMouseEventFilter(QQuickItem *child, QEvent *event)
+{
+ Q_D(QQuickSpinBox);
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ return d->handleMousePressEvent(child, static_cast<QMouseEvent *>(event));
+ case QEvent::MouseMove:
+ return d->handleMouseMoveEvent(child, static_cast<QMouseEvent *>(event));
+ case QEvent::MouseButtonRelease:
+ return d->handleMouseReleaseEvent(child, static_cast<QMouseEvent *>(event));
+ case QEvent::UngrabMouse:
+ return d->handleMouseUngrabEvent(child);
+ default:
+ return false;
+ }
+}
+
+void QQuickSpinBox::timerEvent(QTimerEvent *event)
+{
+ Q_D(QQuickSpinBox);
+ QQuickControl::timerEvent(event);
+ if (event->timerId() == d->delayTimer) {
+ d->startPressRepeat();
+ } else if (event->timerId() == d->repeatTimer) {
+ if (d->up->isPressed())
+ increase();
+ else if (d->down->isPressed())
+ decrease();
+ }
+}
+
+void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QQuickSpinBox);
+ QQuickControl::itemChange(change, value);
+ if (change == ItemActiveFocusHasChanged && !value.boolValue)
+ d->updateValue();
+}
+
+void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
+{
+ Q_UNUSED(oldItem);
+ if (newItem)
+ newItem->setActiveFocusOnTab(true);
+}
+
+class QQuickSpinnerPrivate : public QObjectPrivate
+{
+public:
+ QQuickSpinnerPrivate() : pressed(false), indicator(Q_NULLPTR) { }
+ bool pressed;
+ QQuickItem *indicator;
+};
+
+QQuickSpinner::QQuickSpinner(QQuickSpinBox *parent) :
+ QObject(*(new QQuickSpinnerPrivate), parent)
+{
+}
+
+bool QQuickSpinner::isPressed() const
+{
+ Q_D(const QQuickSpinner);
+ return d->pressed;
+}
+
+void QQuickSpinner::setPressed(bool pressed)
+{
+ Q_D(QQuickSpinner);
+ if (d->pressed != pressed) {
+ d->pressed = pressed;
+ emit pressedChanged();
+ }
+}
+
+QQuickItem *QQuickSpinner::indicator() const
+{
+ Q_D(const QQuickSpinner);
+ return d->indicator;
+}
+
+void QQuickSpinner::setIndicator(QQuickItem *indicator)
+{
+ Q_D(QQuickSpinner);
+ if (d->indicator != indicator) {
+ delete d->indicator;
+ d->indicator = indicator;
+ if (indicator) {
+ if (!indicator->parentItem())
+ indicator->setParentItem(static_cast<QQuickItem *>(parent()));
+ indicator->setAcceptedMouseButtons(Qt::LeftButton);
+ }
+ emit indicatorChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/templates/qquickspinbox_p.h b/src/templates/qquickspinbox_p.h
new file mode 100644
index 00000000..9188d397
--- /dev/null
+++ b/src/templates/qquickspinbox_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Labs Templates 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 QQUICKSPINBOX_P_H
+#define QQUICKSPINBOX_P_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 <QtLabsTemplates/private/qquickcontrol_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSpinner;
+class QQuickSpinnerPrivate;
+class QQuickSpinBoxPrivate;
+
+class Q_LABSTEMPLATES_EXPORT QQuickSpinBox : public QQuickControl
+{
+ Q_OBJECT
+ Q_PROPERTY(int from READ from WRITE setFrom NOTIFY fromChanged FINAL)
+ Q_PROPERTY(int to READ to WRITE setTo NOTIFY toChanged FINAL)
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
+ Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
+ Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged FINAL)
+ Q_PROPERTY(QQuickSpinner *up READ up CONSTANT FINAL)
+ Q_PROPERTY(QQuickSpinner *down READ down CONSTANT FINAL)
+
+public:
+ explicit QQuickSpinBox(QQuickItem *parent = Q_NULLPTR);
+
+ int from() const;
+ void setFrom(int from);
+
+ int to() const;
+ void setTo(int to);
+
+ int value() const;
+ void setValue(int value);
+
+ int stepSize() const;
+ void setStepSize(int step);
+
+ QLocale locale() const;
+ void setLocale(const QLocale &locale);
+
+ QQuickSpinner *up() const;
+ QQuickSpinner *down() const;
+
+public Q_SLOTS:
+ void increase();
+ void decrease();
+
+Q_SIGNALS:
+ void fromChanged();
+ void toChanged();
+ void valueChanged();
+ void stepSizeChanged();
+ void localeChanged();
+
+protected:
+ bool childMouseEventFilter(QQuickItem *child, QEvent *event) Q_DECL_OVERRIDE;
+ void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
+ void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
+ void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
+
+ void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
+ void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) Q_DECL_OVERRIDE;
+
+private:
+ Q_DISABLE_COPY(QQuickSpinBox)
+ Q_DECLARE_PRIVATE(QQuickSpinBox)
+};
+
+class Q_LABSTEMPLATES_EXPORT QQuickSpinner : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL)
+ Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL)
+
+public:
+ explicit QQuickSpinner(QQuickSpinBox *parent);
+
+ bool isPressed() const;
+ void setPressed(bool pressed);
+
+ QQuickItem *indicator() const;
+ void setIndicator(QQuickItem *indicator);
+
+Q_SIGNALS:
+ void pressedChanged();
+ void indicatorChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickSpinner)
+ Q_DECLARE_PRIVATE(QQuickSpinner)
+};
+
+Q_DECLARE_TYPEINFO(QQuickSpinBox, Q_COMPLEX_TYPE);
+Q_DECLARE_TYPEINFO(QQuickSpinner, Q_COMPLEX_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSPINBOX_P_H
diff --git a/src/templates/templates.pri b/src/templates/templates.pri
index 48405c42..8dc9764d 100644
--- a/src/templates/templates.pri
+++ b/src/templates/templates.pri
@@ -27,6 +27,7 @@ HEADERS += \
$$PWD/qquickscrollbar_p.h \
$$PWD/qquickscrollindicator_p.h \
$$PWD/qquickslider_p.h \
+ $$PWD/qquickspinbox_p.h \
$$PWD/qquickstackview_p.h \
$$PWD/qquickstackview_p_p.h \
$$PWD/qquickswipeview_p.h \
@@ -63,6 +64,7 @@ SOURCES += \
$$PWD/qquickscrollbar.cpp \
$$PWD/qquickscrollindicator.cpp \
$$PWD/qquickslider.cpp \
+ $$PWD/qquickspinbox.cpp \
$$PWD/qquickstackview.cpp \
$$PWD/qquickstackview_p.cpp \
$$PWD/qquickswipeview.cpp \
diff --git a/tests/auto/accessibility/data/spinbox.qml b/tests/auto/accessibility/data/spinbox.qml
new file mode 100644
index 00000000..abaee9ca
--- /dev/null
+++ b/tests/auto/accessibility/data/spinbox.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.5
+import QtQuick.Window 2.2
+import Qt.labs.controls 1.0
+
+Window {
+ visible: true
+
+ SpinBox {
+ id: spinbox
+ objectName: "spinbox"
+ from: 0
+ to: 100
+ value: 50
+ stepSize: 1
+ }
+}
diff --git a/tests/auto/accessibility/tst_accessibility.cpp b/tests/auto/accessibility/tst_accessibility.cpp
index 873a86c1..a9e04d59 100644
--- a/tests/auto/accessibility/tst_accessibility.cpp
+++ b/tests/auto/accessibility/tst_accessibility.cpp
@@ -82,6 +82,7 @@ void tst_accessibility::a11y_data()
QTest::newRow("ScrollBar") << "scrollbar" << 0x00000003 << ""; //QAccessible::ScrollBar
QTest::newRow("ScrollIndicator") << "scrollindicator" << 0x00000027 << ""; //QAccessible::Indicator
QTest::newRow("Slider") << "slider" << 0x00000033 << ""; //QAccessible::Slider
+ QTest::newRow("SpinBox") << "spinbox" << 0x00000034 << ""; //QAccessible::SpinBox
// StackView
QTest::newRow("Switch") << "switch" << 0x0000002B << "Switch"; //QAccessible::Button
QTest::newRow("TabBar") << "tabbar" << 0x0000003C << ""; //QAccessible::PageTabList
diff --git a/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml b/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml
index 5ab0191e..cf107373 100644
--- a/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml
+++ b/tests/auto/activeFocusOnTab/data/activeFocusOnTab.qml
@@ -141,6 +141,11 @@ Item {
objectName: "slider"
value: 0.5
}
+ SpinBox {
+ id: spinbox
+ objectName: "spinbox"
+ value: 50
+ }
// StackView
Switch {
id: swtich // switch
diff --git a/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp b/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp
index 88607514..c618a2d5 100644
--- a/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp
+++ b/tests/auto/activeFocusOnTab/tst_activeFocusOnTab.cpp
@@ -191,7 +191,16 @@ void tst_activeFocusOnTab::allControls()
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
- // Tab: slider->switch
+ // Tab: slider->spinbox
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
+ QGuiApplication::sendEvent(window, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QQuickItem>(window->rootObject(), "spinbox");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // Tab: spinbox->switch
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
@@ -290,7 +299,16 @@ void tst_activeFocusOnTab::allControls()
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
- // BackTab: switch->switch
+ // BackTab: switch->spinbox
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
+ QGuiApplication::sendEvent(window, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QQuickItem>(window->rootObject(), "spinbox");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // BackTab: spinbox->slider
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
@@ -450,7 +468,16 @@ void tst_activeFocusOnTab::textControls()
QVERIFY(item);
QVERIFY(item->hasActiveFocus());
- // BackTab: textfield->textarea
+ // BackTab: textfield->spinbox
+ key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
+ QGuiApplication::sendEvent(window, &key);
+ QVERIFY(key.isAccepted());
+
+ item = findItem<QQuickItem>(window->rootObject(), "spinbox");
+ QVERIFY(item);
+ QVERIFY(item->hasActiveFocus());
+
+ // BackTab: spinbox->textarea
key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1);
QGuiApplication::sendEvent(window, &key);
QVERIFY(key.isAccepted());
diff --git a/tests/auto/controls/data/tst_spinbox.qml b/tests/auto/controls/data/tst_spinbox.qml
new file mode 100644
index 00000000..b5d85dbd
--- /dev/null
+++ b/tests/auto/controls/data/tst_spinbox.qml
@@ -0,0 +1,301 @@
+/****************************************************************************
+**
+** 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 Qt.labs.controls 1.0
+
+TestCase {
+ id: testCase
+ width: 400
+ height: 400
+ visible: true
+ when: windowShown
+ name: "SpinBox"
+
+ SignalSpy{
+ id: upPressedSpy
+ signalName: "pressedChanged"
+ }
+
+ SignalSpy{
+ id: downPressedSpy
+ signalName: "pressedChanged"
+ }
+
+ Component {
+ id: spinBox
+ SpinBox { }
+ }
+
+ function init() {
+ verify(!upPressedSpy.target)
+ compare(upPressedSpy.count, 0)
+ verify(!downPressedSpy.target)
+ compare(downPressedSpy.count, 0)
+ }
+
+ function cleanup() {
+ upPressedSpy.target = null
+ upPressedSpy.clear()
+ downPressedSpy.target = null
+ downPressedSpy.clear()
+ }
+
+ function test_defaults() {
+ var control = spinBox.createObject(testCase)
+ verify(control)
+
+ compare(control.from, 0)
+ compare(control.to, 99)
+ compare(control.value, 0)
+ compare(control.stepSize, 1)
+ compare(control.up.pressed, false)
+ compare(control.down.pressed, false)
+
+ control.destroy()
+ }
+
+ function test_value() {
+ var control = spinBox.createObject(testCase)
+ verify(control)
+
+ compare(control.value, 0)
+ control.value = 50
+ compare(control.value, 50)
+ control.value = 99
+ compare(control.value, 99)
+ control.value = -99
+ compare(control.value, 0)
+ control.value = 100
+ compare(control.value, 99)
+
+ control.destroy()
+ }
+
+ function test_range() {
+ var control = spinBox.createObject(testCase, {from: 0, to: 100, value: 50})
+ verify(control)
+
+ compare(control.from, 0)
+ compare(control.to, 100)
+ compare(control.value, 50)
+
+ control.value = 1000
+ compare(control.value, 100)
+
+ control.value = -1
+ compare(control.value, 0)
+
+ control.from = 25
+ compare(control.from, 25)
+ compare(control.value, 25)
+
+ control.to = 75
+ compare(control.to, 75)
+ compare(control.value, 25)
+
+ control.value = 50
+ compare(control.value, 50)
+
+ control.destroy()
+ }
+
+ function test_inverted() {
+ var control = spinBox.createObject(testCase, {from: 100, to: -100})
+ verify(control)
+
+ compare(control.from, 100)
+ compare(control.to, -100)
+ compare(control.value, 0)
+
+ control.value = 200
+ compare(control.value, 100)
+
+ control.value = -200
+ compare(control.value, -100)
+
+ control.value = 0
+ compare(control.value, 0)
+
+ control.destroy()
+ }
+
+ function test_mouse() {
+ var control = spinBox.createObject(testCase, {stepSize: 50})
+ verify(control)
+
+ upPressedSpy.target = control.up
+ verify(upPressedSpy.valid)
+
+ mousePress(control.up.indicator)
+ compare(upPressedSpy.count, 1)
+ compare(control.up.pressed, true)
+ compare(downPressedSpy.count, 0)
+ compare(control.down.pressed, false)
+ compare(control.value, 0)
+
+ mouseRelease(control.up.indicator)
+ compare(upPressedSpy.count, 2)
+ compare(control.up.pressed, false)
+ compare(downPressedSpy.count, 0)
+ compare(control.down.pressed, false)
+ compare(control.value, 50)
+
+ downPressedSpy.target = control.down
+ verify(downPressedSpy.valid)
+
+ mousePress(control.down.indicator)
+ compare(downPressedSpy.count, 1)
+ compare(control.down.pressed, true)
+ compare(upPressedSpy.count, 2)
+ compare(control.up.pressed, false)
+ compare(control.value, 50)
+
+ mouseRelease(control.down.indicator)
+ compare(downPressedSpy.count, 2)
+ compare(control.down.pressed, false)
+ compare(upPressedSpy.count, 2)
+ compare(control.up.pressed, false)
+ compare(control.value, 0)
+
+ control.destroy()
+ }
+
+ function test_keys() {
+ var control = spinBox.createObject(testCase)
+ verify(control)
+
+ var upPressedCount = 0
+ var downPressedCount = 0
+
+ upPressedSpy.target = control.up
+ verify(upPressedSpy.valid)
+
+ downPressedSpy.target = control.down
+ verify(downPressedSpy.valid)
+
+ control.forceActiveFocus()
+ verify(control.activeFocus)
+
+ control.value = 50
+ compare(control.value, 50)
+
+ for (var d1 = 1; d1 <= 10; ++d1) {
+ keyPress(Qt.Key_Down)
+ compare(control.down.pressed, true)
+ compare(control.up.pressed, false)
+ compare(downPressedSpy.count, ++downPressedCount)
+
+ compare(control.value, 50 - d1)
+
+ keyRelease(Qt.Key_Down)
+ compare(control.down.pressed, false)
+ compare(control.up.pressed, false)
+ compare(downPressedSpy.count, ++downPressedCount)
+ }
+ compare(control.value, 40)
+
+ for (var i1 = 1; i1 <= 10; ++i1) {
+ keyPress(Qt.Key_Up)
+ compare(control.up.pressed, true)
+ compare(control.down.pressed, false)
+ compare(upPressedSpy.count, ++upPressedCount)
+
+ compare(control.value, 40 + i1)
+
+ keyRelease(Qt.Key_Up)
+ compare(control.down.pressed, false)
+ compare(control.up.pressed, false)
+ compare(upPressedSpy.count, ++upPressedCount)
+ }
+ compare(control.value, 50)
+
+ control.stepSize = 25
+ compare(control.stepSize, 25)
+
+ for (var d2 = 1; d2 <= 10; ++d2) {
+ keyPress(Qt.Key_Down)
+ compare(control.down.pressed, true)
+ compare(control.up.pressed, false)
+ compare(downPressedSpy.count, ++downPressedCount)
+
+ compare(control.value, Math.max(0, 50 - d2 * 25))
+
+ keyRelease(Qt.Key_Down)
+ compare(control.down.pressed, false)
+ compare(control.up.pressed, false)
+ compare(downPressedSpy.count, ++downPressedCount)
+ }
+ compare(control.value, 0)
+
+ for (var i2 = 1; i2 <= 10; ++i2) {
+ keyPress(Qt.Key_Up)
+ compare(control.up.pressed, true)
+ compare(control.down.pressed, false)
+ compare(upPressedSpy.count, ++upPressedCount)
+
+ compare(control.value, Math.min(99, i2 * 25))
+
+ keyRelease(Qt.Key_Up)
+ compare(control.down.pressed, false)
+ compare(control.up.pressed, false)
+ compare(upPressedSpy.count, ++upPressedCount)
+ }
+ compare(control.value, 99)
+
+ control.destroy()
+ }
+
+ function test_locale() {
+ var control = spinBox.createObject(testCase)
+ verify(control)
+
+ control.locale = Qt.locale("ar_EG") // Arabic, Egypt
+
+ var numbers = ["٠", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"]
+ for (var i = 0; i < 10; ++i) {
+ control.value = i
+ compare(control.contentItem.text, numbers[i])
+ }
+
+ control.destroy()
+ }
+}
diff --git a/tests/auto/snippets/data/qtlabscontrols-spinbox-background.qml b/tests/auto/snippets/data/qtlabscontrols-spinbox-background.qml
new file mode 100644
index 00000000..65545970
--- /dev/null
+++ b/tests/auto/snippets/data/qtlabscontrols-spinbox-background.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+import Qt.labs.controls 1.0
+
+SpinBox {
+ value: 50
+ Rectangle {
+ anchors.fill: parent
+ color: "transparent"
+ border.color: "red"
+ }
+}
diff --git a/tests/auto/snippets/data/qtlabscontrols-spinbox-contentItem.qml b/tests/auto/snippets/data/qtlabscontrols-spinbox-contentItem.qml
new file mode 100644
index 00000000..e0fb39f4
--- /dev/null
+++ b/tests/auto/snippets/data/qtlabscontrols-spinbox-contentItem.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+import Qt.labs.controls 1.0
+
+SpinBox {
+ value: 50
+ Rectangle {
+ anchors.fill: contentItem
+ color: "transparent"
+ border.color: "red"
+ }
+}
diff --git a/tests/auto/snippets/data/qtlabscontrols-spinbox-down.qml b/tests/auto/snippets/data/qtlabscontrols-spinbox-down.qml
new file mode 100644
index 00000000..b6e78c83
--- /dev/null
+++ b/tests/auto/snippets/data/qtlabscontrols-spinbox-down.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+import Qt.labs.controls 1.0
+
+SpinBox {
+ value: 50
+ Rectangle {
+ anchors.fill: down.indicator
+ color: "transparent"
+ border.color: "red"
+ }
+}
diff --git a/tests/auto/snippets/data/qtlabscontrols-spinbox-up.qml b/tests/auto/snippets/data/qtlabscontrols-spinbox-up.qml
new file mode 100644
index 00000000..6f150b56
--- /dev/null
+++ b/tests/auto/snippets/data/qtlabscontrols-spinbox-up.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+import Qt.labs.controls 1.0
+
+SpinBox {
+ value: 50
+ Rectangle {
+ anchors.fill: up.indicator
+ color: "transparent"
+ border.color: "red"
+ }
+}
diff --git a/tests/auto/snippets/data/qtlabscontrols-spinbox.qml b/tests/auto/snippets/data/qtlabscontrols-spinbox.qml
new file mode 100644
index 00000000..588dce65
--- /dev/null
+++ b/tests/auto/snippets/data/qtlabscontrols-spinbox.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.0
+import Qt.labs.controls 1.0
+
+//! [1]
+SpinBox {
+ value: 50
+}
+//! [1]
diff --git a/tests/benchmarks/objectcount/tst_objectcount.cpp b/tests/benchmarks/objectcount/tst_objectcount.cpp
index 3b5db87d..b2170cd5 100644
--- a/tests/benchmarks/objectcount/tst_objectcount.cpp
+++ b/tests/benchmarks/objectcount/tst_objectcount.cpp
@@ -209,6 +209,10 @@ void tst_ObjectCount::testCount_data()
<< QByteArray("import QtQuick.Controls 1.3; Slider { }")
<< QByteArray("import Qt.labs.controls 1.0; Slider { }");
+ QTest::newRow("SpinBox")
+ << QByteArray("import QtQuick.Controls 1.3; SpinBox { }")
+ << QByteArray("import Qt.labs.controls 1.0; SpinBox { }");
+
QTest::newRow("StackView")
<< QByteArray("import QtQuick.Controls 1.3; StackView { }")
<< QByteArray("import Qt.labs.controls 1.0; StackView { }");