From 77d1dcca1b43ea38d7a6e58206d06004b77a8103 Mon Sep 17 00:00:00 2001
From: Caroline Chao <caroline.chao@digia.com>
Date: Tue, 12 Feb 2013 17:12:28 +0100
Subject: [PATCH] SpinBox: Refactoring API

Clean the SpinBox API
Add new property decimals.

Add missing documentation.
Update autotests and manual test for SpinBox.

Change-Id: Ia977ab5c142095a0dbb0ddc452b0dc77d7c133f8
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
---
 src/qtdesktop/SpinBox.qml                   | 184 +++++++++-----------
 src/styles/Desktop/SpinBoxStyle.qml         |  10 +-
 src/styles/SpinBoxStyle.qml                 |   8 +-
 tests/auto/qtdesktop/data/tst_spinbox.qml   |  92 ++++++++--
 tests/manual/controls/pages/SpinBoxPage.qml |  55 ++++--
 tests/manual/controls/shared/SetupField.qml |   2 +-
 6 files changed, 206 insertions(+), 145 deletions(-)

diff --git a/src/qtdesktop/SpinBox.qml b/src/qtdesktop/SpinBox.qml
index b98ce6d79..e8adce999 100644
--- a/src/qtdesktop/SpinBox.qml
+++ b/src/qtdesktop/SpinBox.qml
@@ -51,14 +51,20 @@ import "Styles/Settings.js" as Settings
     SpinBox allows the user to choose a value by clicking the up/down buttons or pressing up/down on the keyboard to increase/decrease
     the value currently displayed. The user can also type the value in manually.
 
-    By default the SpinBox provides discrete values in the range [0-99] with a stepSize of 1.0.
+    By default the SpinBox provides discrete values in the range [0-99] with a \l stepSize of 1 and 0 \l decimals.
 
     \code
     SpinBox {
         id: spinbox
-        minimumValue: 0
-        maximumValue: 20
-        stepSize: 1.0
+    }
+    \endcode
+
+    Note that if you require decimal values you will need to set the \l decimals to a non 0 value.
+
+    \code
+    SpinBox {
+        id: spinbox
+        decimals: 2
     }
     \endcode
 
@@ -93,66 +99,81 @@ FocusScope {
     /*!
         The amount by which the \l value is incremented/decremented when a
         spin button is pressed.
-    */
-    property real singleStep: 1.0
-
-    /*!
-        \qmlproperty string SpinBox::inputMask
 
-        The input mask for the text input. See \l TextInput
+        The default value is 1.0.
     */
-    property alias inputMask: input.inputMask
+    property real stepSize: 1.0
 
-    /*!
-        The suffix for the text content.
-    */
+    /*! The suffix for the value. I.e "cm" */
     property string suffix
-    //property string prefix ### not implemented
-
-    /*! \internal */
-    property var styleHints:[]
 
-    /*!
-        This property indicates if the up/increment button is currently enabled.
-    */
-    readonly property bool upEnabled: value != maximumValue;
+    /*! The prefix for the value. I.e "$" */
+    property string prefix
 
-    /*!
-        This property indicates if the down/decrement button is currently enabled.
+    /*! This property indicates the amount of decimals.
+      Note that if you enter more decimals than specified, they will
+      be truncated to the specified amount of decimal places.
+      The default value is \c 0
     */
-    readonly property bool downEnabled: value != minimumValue;
+    property int decimals: 0
 
-    /*!
-        \qmlproperty bool SpinBox::upPressed
+    /*! \qmlproperty font SpinBox::font
 
-        This property indicates if the up/increment button is currently being pressed.
+        This property indicates the current font used by the SpinBox.
     */
-    readonly property alias upPressed: mouseUp.pressed
+    property alias font: input.font
 
-    /*!
-        \qmlproperty bool SpinBox::downPressed
 
-        This property indicates if the down/decrement button is currently being pressed.
-    */
-    readonly property alias downPressed: mouseDown.pressed
+    /*! \internal */
+    property Component style: Qt.createComponent(Settings.THEME_PATH + "/SpinBoxStyle.qml", spinbox)
 
-    // These are currently only needed for styling
+    /*! \internal */
+    function __increment() {
+        input.setValue(input.text)
+        value += stepSize
+        if (value > maximumValue)
+            value = maximumValue
+        input.text = value.toFixed(decimals)
+    }
 
+    /*! \internal */
+    function __decrement() {
+        input.setValue(input.text)
+        value -= stepSize
+        if (value < minimumValue)
+            value = minimumValue
+        input.text =  value.toFixed(decimals)
+    }
+
+    /*! \internal */
+    readonly property bool __upEnabled: value != maximumValue;
+    /*! \internal */
+    readonly property bool __downEnabled: value != minimumValue;
+    /*! \internal */
+    readonly property alias __upPressed: mouseUp.pressed
+    /*! \internal */
+    readonly property alias __downPressed: mouseDown.pressed
+    /*! \internal */
+    property var styleHints:[]
     /*! \internal */
     property alias __upHovered: mouseUp.containsMouse
     /*! \internal */
     property alias __downHovered: mouseDown.containsMouse
     /*! \internal */
     property alias __containsMouse: mouseArea.containsMouse
-
-    /*! \qmlproperty font SpinBox::font
-
-        This property indicates the current font used by the SpinBox.
-    */
-    property alias font: input.font
+    /*! \internal */
+    property alias __text: input.text
 
     /*! \internal */
-    property Component style: Qt.createComponent(Settings.THEME_PATH + "/SpinBoxStyle.qml", spinbox)
+    onDecimalsChanged: input.setValue(value)
+    /*! \internal */
+    onMaximumValueChanged: input.setValue(value)
+    /*! \internal */
+    onMinimumValueChanged: input.setValue(value)
+    /*! \internal */
+    Component.onCompleted: input.setValue(value)
+    /*! \internal */
+    onValueChanged: input.setValue(value)
 
     Accessible.name: input.text
     Accessible.role: Accessible.SpinBox
@@ -163,54 +184,6 @@ FocusScope {
     implicitWidth: loader.item ? loader.item.implicitWidth : 0
     implicitHeight: loader.item ? loader.item.implicitHeight : 0
 
-    /*!
-        Increments \l value by \l singleStep, clamping to \l maximumValue
-        if the new value is too large.
-    */
-    function increment() {
-        setValue(input.text)
-        value += singleStep
-        if (value > maximumValue)
-            value = maximumValue
-        input.text = value
-    }
-
-    /*!
-        Increments \l value by \l singleStep, clamping to \l minimumValue
-        if the new value is too small.
-    */
-    function decrement() {
-        setValue(input.text)
-        value -= singleStep
-        if (value < minimumValue)
-            value = minimumValue
-        input.text = value
-    }
-
-    /*!
-        Sets \l value to \a v, clamping to \l minimumValue and \l maximumValue
-        if \a v is not within this range.
-    */
-    function setValue(v) {
-        var newval = parseFloat(v)
-        if (newval > maximumValue)
-            newval = maximumValue
-        else if (v < minimumValue)
-            newval = minimumValue
-        value = newval
-        input.text = value
-    }
-
-    /*! \internal */
-    Component.onCompleted: setValue(value)
-
-    /*! \internal */
-    onValueChanged: {
-        input.valueUpdate = true
-        input.text = value
-        input.valueUpdate = false
-    }
-
     Loader {
         id: loader
         property alias control: spinbox
@@ -224,12 +197,25 @@ FocusScope {
         hoverEnabled: true
     }
 
-    // Spinbox input field
-
     TextInput {
         id: input
 
-        property bool valueUpdate: false
+        function setValue(v) {
+            var newval = parseFloat(v)
+
+            if (!isNaN(newval)) {
+                if (newval > maximumValue)
+                    newval = maximumValue
+                else if (v < minimumValue)
+                    newval = minimumValue
+                newval = newval.toFixed(decimals)
+                spinbox.value = parseFloat(newval)
+                input.text = newval
+            } else {
+                input.text = parseFloat(spinbox.value)
+            }
+        }
+
         property Item styleItem: loader.item
 
         clip: true
@@ -243,8 +229,8 @@ FocusScope {
         anchors.bottomMargin: styleItem ? styleItem.bottomMargin: 0
         selectByMouse: true
 
-        // validator: DoubleValidator { bottom: minimumValue; top: maximumValue; }
-        onAccepted: {setValue(input.text)}
+        validator: DoubleValidator { bottom: minimumValue; top: maximumValue; }
+        onAccepted: setValue(input.text)
         onActiveFocusChanged: setValue(input.text)
         color: loader.item ? loader.item.foregroundColor : "black"
         selectionColor: loader.item ? loader.item.selectionColor : "black"
@@ -278,7 +264,7 @@ FocusScope {
         width: upRect ? upRect.width : 0
         height: upRect ? upRect.height : 0
 
-        onClicked: increment()
+        onClicked: __increment()
 
         property bool autoincrement: false;
         onReleased: autoincrement = false
@@ -292,7 +278,7 @@ FocusScope {
         id: mouseDown
         hoverEnabled: true
 
-        onClicked: decrement()
+        onClicked: __decrement()
         property var downRect: loader.item ? loader.item.downRect : null
 
         anchors.left: parent.left
@@ -310,6 +296,6 @@ FocusScope {
         Timer { running: mouseDown.autoincrement; interval: 60 ; repeat: true ; onTriggered: decrement() }
     }
 
-    Keys.onUpPressed: increment()
-    Keys.onDownPressed: decrement()
+    Keys.onUpPressed: __increment()
+    Keys.onDownPressed: __decrement()
 }
diff --git a/src/styles/Desktop/SpinBoxStyle.qml b/src/styles/Desktop/SpinBoxStyle.qml
index 31ff0a6ab..dd372c855 100644
--- a/src/styles/Desktop/SpinBoxStyle.qml
+++ b/src/styles/Desktop/SpinBoxStyle.qml
@@ -111,15 +111,15 @@ Item {
         id: styleitem
         elementType: "spinbox"
         anchors.fill: parent
-        sunken: (downEnabled && downPressed) | (upEnabled && upPressed)
+        sunken: (control.__downEnabled && control.__downPressed) || (control.__upEnabled && control.__upPressed)
         hover: __containsMouse
         hints: control.styleHints
         hasFocus: control.focus
         enabled: control.enabled
-        value: (upPressed ? 1 : 0)           |
-               (downPressed == 1 ? 1<<1 : 0) |
-               (upEnabled ? (1<<2) : 0)      |
-               (downEnabled == 1 ? (1<<3) : 0)
+        value: (control.__upPressed ? 1 : 0)           |
+               (control.__downPressed == 1 ? 1<<1 : 0) |
+               (control.__upEnabled ? (1<<2) : 0)      |
+               (control.__downEnabled == 1 ? (1<<3) : 0)
         contentWidth: 200
         contentHeight: 25
     }
diff --git a/src/styles/SpinBoxStyle.qml b/src/styles/SpinBoxStyle.qml
index 5b3c2f9be..e08025786 100644
--- a/src/styles/SpinBoxStyle.qml
+++ b/src/styles/SpinBoxStyle.qml
@@ -75,8 +75,8 @@ Item {
         anchors.centerIn: parent
         implicitWidth: 12
         gradient: Gradient {
-            GradientStop {color: control.upPressed ? "lightgray" : "white" ; position: 0}
-            GradientStop {color: control.upPressed ? "lightgray" : "lightgray" ; position: 1}
+            GradientStop {color: control.__upPressed ? "lightgray" : "white" ; position: 0}
+            GradientStop {color: control.__upPressed ? "lightgray" : "lightgray" ; position: 1}
         }
         border.color: Qt.darker(backgroundColor, 2)
         Image {
@@ -88,8 +88,8 @@ Item {
     property Component downControl: Rectangle {
         implicitWidth: 12
         gradient: Gradient {
-            GradientStop {color: control.downPressed ? "lightgray" : "white" ; position: 0}
-            GradientStop {color: control.downPressed ? "lightgray" : "lightgray" ; position: 1}
+            GradientStop {color: control.__downPressed ? "lightgray" : "white" ; position: 0}
+            GradientStop {color: control.__downPressed ? "lightgray" : "lightgray" ; position: 1}
         }
         border.color: Qt.darker(backgroundColor, 2)
         Image {
diff --git a/tests/auto/qtdesktop/data/tst_spinbox.qml b/tests/auto/qtdesktop/data/tst_spinbox.qml
index 058bec87e..4299b1b5b 100644
--- a/tests/auto/qtdesktop/data/tst_spinbox.qml
+++ b/tests/auto/qtdesktop/data/tst_spinbox.qml
@@ -60,7 +60,7 @@ Item {
             spinbox.forceActiveFocus()
 
             compare(spinbox.maximumValue, 50)
-            spinbox.setValue(spinbox.maximumValue - 3)
+            spinbox.value = spinbox.maximumValue - 3
             keyPress(Qt.Key_Up)
             compare(spinbox.value, spinbox.maximumValue - 2)
             keyPress(Qt.Key_Up)
@@ -75,7 +75,7 @@ Item {
             spinbox.forceActiveFocus()
 
             compare(spinbox.minimumValue, 10)
-            spinbox.setValue(spinbox.minimumValue + 3)
+            spinbox.value = spinbox.minimumValue + 3
             keyPress(Qt.Key_Down)
             compare(spinbox.value, spinbox.minimumValue + 2)
             keyPress(Qt.Key_Down)
@@ -90,7 +90,7 @@ Item {
             spinbox.forceActiveFocus()
             setCoordinates(spinbox)
 
-            spinbox.setValue(spinbox.maximumValue - 3)
+            spinbox.value = spinbox.maximumValue - 3
             mouseClick(spinbox, upCoord.x, upCoord.y, Qt.LeftButton)
             compare(spinbox.value, spinbox.maximumValue - 2)
             mouseClick(spinbox, upCoord.x, upCoord.y, Qt.LeftButton)
@@ -105,7 +105,7 @@ Item {
             spinbox.forceActiveFocus()
             setCoordinates(spinbox)
 
-            spinbox.setValue(spinbox.minimumValue + 3)
+            spinbox.value = spinbox.minimumValue + 3
             mouseClick(spinbox, downCoord.x, downCoord.y, Qt.LeftButton)
             compare(spinbox.value, spinbox.minimumValue + 2)
             mouseClick(spinbox, downCoord.x, downCoord.y, Qt.LeftButton)
@@ -141,42 +141,98 @@ Item {
 
         function test_maxvalue() {
             var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
-            spinbox.setValue(spinbox.maximumValue + 1)
+            spinbox.value = spinbox.maximumValue + 1
             compare(spinbox.value, spinbox.maximumValue)
         }
 
         function test_minvalue() {
             var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
-            spinbox.setValue(spinbox.minimumValue - 1)
+            spinbox.value = spinbox.minimumValue - 1
             compare(spinbox.value, spinbox.minimumValue)
         }
 
-        function test_invalidvalue() {
+        function test_nanvalue() {
             var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
-            spinbox.setValue("hello")
-            compare(spinbox.value.toString().toLowerCase(), "nan")
+            // It is not possible to set a string to the spinbox value.
+            // Nan is a valid number though
+            spinbox.value = NaN
+            compare(spinbox.value, NaN)
+            compare(spinbox.__text, "nan")
         }
 
-        function test_negativesinglestep()
+        function test_decimals() {
+            var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
+
+            spinbox.decimals = 0
+            spinbox.value = 1.00001
+            compare(spinbox.value, 1)
+            compare(spinbox.__text, "1")
+
+            spinbox.decimals = 1
+            spinbox.value = 1.00001
+            compare(spinbox.value, 1)
+            compare(spinbox.__text, "1.0")
+            spinbox.value = 1.1
+            compare(spinbox.value, 1.1)
+            compare(spinbox.__text, "1.1")
+
+            spinbox.decimals = 5
+            spinbox.value = 1.00001
+            compare(spinbox.value, 1.00001)
+            compare(spinbox.__text, "1.00001")
+
+            spinbox.decimals = 6
+            compare(spinbox.value, 1.00001)
+            compare(spinbox.__text, "1.000010")
+        }
+
+        function test_stepsize()
         {
             var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
             spinbox.forceActiveFocus()
 
-            spinbox.singleStep = -1
-            spinbox.setValue(5)
+            spinbox.stepSize = 2
+            spinbox.value = 10
 
-            compare(spinbox.value, 5)
+            compare(spinbox.value, 10)
+
+            keyPress(Qt.Key_Up)
+            compare(spinbox.value, 10 + spinbox.stepSize)
 
             var previousValue = spinbox.value
+            keyPress(Qt.Key_Down)
+            compare(spinbox.value, previousValue - spinbox.stepSize)
+        }
+
+        function test_negativeStepSize()
+        {
+            var spinbox = Qt.createQmlObject('import QtDesktop 1.0; SpinBox {}', container, '')
+            spinbox.forceActiveFocus()
+
+            spinbox.minimumValue = -50
+            spinbox.maximumValue = 50
+
+            spinbox.stepSize = -2
+            spinbox.value = 5
+
+            compare(spinbox.value, 5)
+
             keyPress(Qt.Key_Up)
+            compare(spinbox.value, 5 + spinbox.stepSize)
+
+            var previousValue = spinbox.value
+            keyPress(Qt.Key_Down)
+            compare(spinbox.value, previousValue - spinbox.stepSize)
+
+            // test on the edges
 
-            expectFailContinue("", "QTCOMPONENTS-1284 - sign of singleStep should be ignored when incrementing value")
-            compare(spinbox.value, spinbox.value + Math.abs(spinbox.singleStep))
+            spinbox.value = -49
             keyPress(Qt.Key_Up)
+            compare(spinbox.value, spinbox.minimumValue)
 
-            previousValue = spinbox.value
-            expectFailContinue("", "QTCOMPONENTS-1284 - sign of singleStep should be ignored when decrementing value")
-            compare(spinbox.value, previousValue - Math.abs(spinbox.singleStep))
+            spinbox.value = 49
+            keyPress(Qt.Key_Down)
+            compare(spinbox.value, spinbox.maximumValue)
         }
 
         function setCoordinates(item)
diff --git a/tests/manual/controls/pages/SpinBoxPage.qml b/tests/manual/controls/pages/SpinBoxPage.qml
index 0573a621a..32b58c7d2 100644
--- a/tests/manual/controls/pages/SpinBoxPage.qml
+++ b/tests/manual/controls/pages/SpinBoxPage.qml
@@ -84,9 +84,9 @@ Page {
 
                 SetupField {
                     id: singleStep
-                    property variant defaultValue: spinbox.singleStep
+                    property variant defaultValue: spinbox.stepSize
                     property string title: "Step"
-                    onValidated: spinbox.singleStep = validatedValue
+                    onValidated: spinbox.stepSize = validatedValue
                 }
 
                 SetupField {
@@ -103,14 +103,7 @@ Page {
                     property string title: "Prefix"
                     property bool isText: true
                     enabled: false // not yet implemented
-                    // onValidated: spinbox.prefix = validatedValue
-                }
-
-                SetupField {
-                    id: inputmask
-                    property variant defaultValue: spinbox.inputMask
-                    property string title: "inputMask"
-                    onValidated: spinbox.inputMask = validatedValue
+                    onValidated: spinbox.prefix = validatedValue
                 }
 
                 SetupField {
@@ -119,6 +112,32 @@ Page {
                     property string title: "Value"
                     onValidated: spinbox.value = validatedValue
                 }
+
+                SetupField {
+                    id: font
+                    property variant defaultValue: spinbox.font.pixelSize
+                    property string title: "font"
+                    onValidated: spinbox.font.pixelSize = validatedValue
+                }
+
+                Item {
+                    height: decimalSpinBox.height
+                    width: parent.width
+
+                    Label {
+                        anchors.left: parent.left
+                        anchors.verticalCenter: parent.verticalCenter
+                        text: "Decimals"
+                        color: "white"
+                    }
+
+                    SpinBox {
+                        id: decimalSpinBox
+                        anchors.right: parent.right
+                        decimals: 0
+                        maximumValue: 5
+                    }
+                }
             }
 
             Column {
@@ -141,7 +160,7 @@ Page {
                 }
 
                 CountField {
-                    id: signalSingleStepChangedCount
+                    id: signalStepSizeChangedCount
                     property string title: "SingleStep"
                 }
 
@@ -173,12 +192,12 @@ Page {
 
             BooleanField {
                 title: "UpEnabled"
-                status: spinbox.upEnabled
+                status: spinbox.__upEnabled
             }
 
             BooleanField {
                 title: "DownEnabled"
-                status: spinbox.downEnabled
+                status: spinbox.__downEnabled
             }
         }
 
@@ -188,12 +207,12 @@ Page {
 
             BooleanField {
                 title: "UpPressed"
-                status: spinbox.upPressed
+                status: spinbox.__upPressed
             }
 
             BooleanField {
                 title: "DownPressed"
-                status: spinbox.downPressed
+                status: spinbox.__downPressed
             }
         }
 
@@ -201,14 +220,14 @@ Page {
             id: spinbox
 
             width: parent.width
+            decimals: decimalSpinBox.value
 
             onMaximumValueChanged: signalMaximumValueChangedCount.increment()
             onMinimumValueChanged: signalMinimumValueChangedCount.increment()
-            onSingleStepChanged: signalSingleStepChangedCount.increment()
+            onStepSizeChanged: signalStepSizeChangedCount.increment()
             onSuffixChanged: signalSuffixChangedCount.increment()
             onValueChanged: signalValueChangedCount.increment()
-            // onPrefixChanged: signalPrefixChangedCount.increment() // not yet implemented
-            onInputMaskChanged:signalInputMaskChangedCount.increment()
+            onPrefixChanged: signalPrefixChangedCount.increment() // not yet implemented
         }
     }
 }
diff --git a/tests/manual/controls/shared/SetupField.qml b/tests/manual/controls/shared/SetupField.qml
index 658b4f8ee..ea7041b19 100644
--- a/tests/manual/controls/shared/SetupField.qml
+++ b/tests/manual/controls/shared/SetupField.qml
@@ -58,7 +58,7 @@ FocusScope {
         anchors.left: parent.left
         anchors.right: field.left
         height: field.height
-        width: 60
+        width: 80
         verticalAlignment: Text.AlignVCenter
         color: enabled? "white" : "grey"
         text: setupField.label + ": "
-- 
GitLab