Преглед изворни кода

ctkDoubleSpinBox now keeps original precision until edited

QDoubleSpinBox rounds the input value with a given decimals number.
This can loose important precision. Here we apply the same mechanism as
for ctkDoubleSlider: store the original value with full precision.

http://www.na-mic.org/Bug/view.php?id=3261
Julien Finet пре 11 година
родитељ
комит
0d291e477b

+ 149 - 24
Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetTest.cpp

@@ -47,8 +47,13 @@ private slots:
 
   void testDecimalsByKey();
   void testDecimalsByKey_data();
+
+  void testDecimalsByShortcuts();
+  void testDecimalsByShortcuts_data();
+
 private:
   void testDecimals(ctkCoordinatesWidget* coordinatesWidget, int decimals);
+  void testDecimals(ctkCoordinatesWidget* coordinatesWidget, QString displayedValues);
 };
 
 // ----------------------------------------------------------------------------
@@ -117,6 +122,21 @@ void ctkCoordinatesWidgetTester
 }
 
 // ----------------------------------------------------------------------------
+void ctkCoordinatesWidgetTester
+::testDecimals(ctkCoordinatesWidget* coordinatesWidget,
+               QString expectedDisplayedText)
+{
+  QStringList displayedText;
+  QList<QDoubleSpinBox*> spinBoxes =
+    coordinatesWidget->findChildren<QDoubleSpinBox*>();
+  foreach(QDoubleSpinBox* spinBox, spinBoxes)
+    {
+    displayedText << spinBox->text();
+    }
+  QCOMPARE(displayedText.join(", "), expectedDisplayedText);
+}
+
+// ----------------------------------------------------------------------------
 void ctkCoordinatesWidgetTester::testDecimalsByValue()
 {
   ctkCoordinatesWidget coordinatesWidget;
@@ -150,7 +170,9 @@ void ctkCoordinatesWidgetTester::testDecimalsByKey()
 {
   ctkCoordinatesWidget coordinatesWidget;
   coordinatesWidget.setDecimalsOption(
-    ctkDoubleSpinBox::DecimalsByValue | ctkDoubleSpinBox::DecimalsByKey);
+    ctkDoubleSpinBox::DecimalsByValue
+    | ctkDoubleSpinBox::DecimalsByKey
+    | ctkDoubleSpinBox::DecimalsByShortcuts);
   coordinatesWidget.setDecimals(3);
   coordinatesWidget.setCoordinates(1.,1.,1.3);
 
@@ -175,17 +197,25 @@ void ctkCoordinatesWidgetTester::testDecimalsByKey()
         text += '.';
         }
       text += '7';
+      lineEdits[0]->setText(text);
       break;
     case 0:
       text = QString("7") + text;
+      lineEdits[0]->setText(text);
       break;
     case -1:
       text.chop(1);
+      lineEdits[0]->setText(text);
+      break;
+    case 100:
+      QTest::keyClick(spinBoxes[0], Qt::Key_Plus, Qt::ControlModifier);
+      break;
+    case -100:
+      QTest::keyClick(spinBoxes[0], Qt::Key_Minus, Qt::ControlModifier);
       break;
     default:
       break;
     }
-  lineEdits[0]->setText(text);
 
   QFETCH(QVector3D, finalDecimals);
   QCOMPARE(spinBoxes[0]->decimals(), static_cast<int>(finalDecimals.x()));
@@ -201,33 +231,128 @@ void ctkCoordinatesWidgetTester::testDecimalsByKey_data()
   QTest::addColumn<int>("decimalsOffset");
   QTest::addColumn<QVector3D>("finalDecimals");
 
-  QTest::newRow("(1, 1, 1) +1") << QVector3D(1., 1., 1.) << 0 << 1 << QVector3D(1,1,1);
-  QTest::newRow("(1, 1, 1) +0") << QVector3D(1., 1., 1.) << 0 << 0 << QVector3D(0,0,0);
-  QTest::newRow("(1, 1, 1) -1") << QVector3D(1., 1., 1.) << 0 << -1 << QVector3D(0,0,0);
-
-  QTest::newRow("(1, 1, 1.3) +1") << QVector3D(1., 1., 1.3) << 1 << 1 << QVector3D(2,2,2);
-  QTest::newRow("(1, 1, 1.3) +0") << QVector3D(1., 1., 1.3) << 1 << 0 << QVector3D(1,1,1);
-  QTest::newRow("(1, 1, 1.3) -1") << QVector3D(1., 1., 1.3) << 1 << -1 << QVector3D(0,1,1);
+  QTest::newRow("(1, 1, 1) +1")
+    << QVector3D(1., 1., 1.) << 0 << 1 << QVector3D(1,1,1);
+  QTest::newRow("(1, 1, 1) +0")
+    << QVector3D(1., 1., 1.) << 0 << 0 << QVector3D(0,0,0);
+  QTest::newRow("(1, 1, 1) -1")
+    << QVector3D(1., 1., 1.) << 0 << -1 << QVector3D(0,0,0);
+  QTest::newRow("(1, 1, 1) ++")
+    << QVector3D(1., 1., 1.) << 0 << 100 << QVector3D(1,1,1);
+  QTest::newRow("(1, 1, 1) --")
+    << QVector3D(1., 1., 1.) << 0 << -100 << QVector3D(0,0,0);
+
+  QTest::newRow("(1, 1, 1.3) +1")
+    << QVector3D(1., 1., 1.3) << 1 << 1 << QVector3D(2,2,2);
+  QTest::newRow("(1, 1, 1.3) +0")
+    << QVector3D(1., 1., 1.3) << 1 << 0 << QVector3D(1,1,1);
+  QTest::newRow("(1, 1, 1.3) -1")
+    << QVector3D(1., 1., 1.3) << 1 << -1 << QVector3D(0,1,1);
+  QTest::newRow("(1, 1, 1.3) ++")
+    << QVector3D(1., 1., 1.3) << 1 << 100 << QVector3D(2,2,2);
+  QTest::newRow("(1, 1, 1.3) --")
+    << QVector3D(1., 1., 1.3) << 1 << -100 << QVector3D(0,1,1);
+
+  QTest::newRow("(1.3, 1, 1) +1")
+    << QVector3D(1.3, 1., 1.) << 1 << 1 << QVector3D(2,2,2);
+  QTest::newRow("(1.3, 1, 1) +0")
+    << QVector3D(1.3, 1., 1.) << 1 << 0 << QVector3D(1,1,1);
+  QTest::newRow("(1.3, 1, 1) -1")
+    << QVector3D(1.3, 1., 1.) << 1 << -1 << QVector3D(0,0,0);
+  QTest::newRow("(1.3, 1, 1) ++")
+    << QVector3D(1.3, 1., 1.) << 1 << 100 << QVector3D(2,2,2);
+  QTest::newRow("(1.3, 1, 1) --")
+    << QVector3D(1.3, 1., 1.) << 1 << -100 << QVector3D(0,0,0);
+
+  QTest::newRow("(1.3, 1, 1.3) +1")
+    << QVector3D(1.3, 1., 1.3) << 1 << 1 << QVector3D(2,2,2);
+  QTest::newRow("(1.3, 1, 1.3) +0")
+    << QVector3D(1.3, 1., 1.3) << 1 << 0 << QVector3D(1,1,1);
+  QTest::newRow("(1.3, 1, 1.3) -1")
+    << QVector3D(1.3, 1., 1.3) << 1 << -1 << QVector3D(0,1,1);
+  QTest::newRow("(1.3, 1, 1.3) ++")
+    << QVector3D(1.3, 1., 1.3) << 1 << 100 << QVector3D(2,2,2);
+  QTest::newRow("(1.3, 1, 1.3) --")
+    << QVector3D(1.3, 1., 1.3) << 1 << -100 << QVector3D(0,1,1);
+
+  QTest::newRow("(1.*, 1, 1.3) +1")
+    << QVector3D(1.12345678910121416, 1., 1.3) << 3 << 1 << QVector3D(4,4,4);
+  QTest::newRow("(1.*, 1, 1.3) +0")
+    << QVector3D(1.12345678910121416, 1., 1.3) << 3 << 0 << QVector3D(3,3,3);
+  QTest::newRow("(1.*, 1, 1.3) -1")
+    << QVector3D(1.12345678910121416, 1., 1.3) << 3 << -1 << QVector3D(2,2,2);
+  QTest::newRow("(1.*, 1, 1.3) ++")
+    << QVector3D(1.12345678910121416, 1., 1.3) << 3 << 100 << QVector3D(4,4,4);
+  QTest::newRow("(1.*, 1, 1.3) --")
+    << QVector3D(1.12345678910121416, 1., 1.3) << 3 << -100 << QVector3D(2,2,2);
+
+  QTest::newRow("(1, 1.*, 1.3) +1")
+    << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 1 << QVector3D(4,4,4);
+  QTest::newRow("(1, 1.*, 1.3) +0")
+    << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 0 << QVector3D(3,3,3);
+  QTest::newRow("(1, 1.*, 1.3) -1")
+    << QVector3D(1., 1.12345678910121416, 1.3) << 3 << -1 << QVector3D(2,3,3);
+  QTest::newRow("(1, 1.*, 1.3) ++")
+    << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 100 << QVector3D(4,4,4);
+  QTest::newRow("(1, 1.*, 1.3) --")
+    << QVector3D(1., 1.12345678910121416, 1.3) << 3 << -100 << QVector3D(2,3,3);
+
+  QTest::newRow("(1.*, 1.*, 1.3) +1")
+    << QVector3D(1.12345678910121416, 1.12345678910121416, 1.3) << 3 << 1 << QVector3D(4,4,4);
+  QTest::newRow("(1.*, 1.*, 1.3) +0")
+    << QVector3D(1.12345678910121416, 1.12345678910121416, 1.3) << 3 << 0 << QVector3D(3,3,3);
+  QTest::newRow("(1.*, 1.*, 1.3) -1")
+    << QVector3D(1.12345678910121416, 1.12345678910121416, 1.3) << 3 << -1 << QVector3D(2,3,3);
+  QTest::newRow("(1.*, 1.*, 1.3) ++")
+    << QVector3D(1.12345678910121416, 1.12345678910121416, 1.3) << 3 << 100 << QVector3D(4,4,4);
+  QTest::newRow("(1.*, 1.*, 1.3) --")
+    << QVector3D(1.12345678910121416, 1.12345678910121416, 1.3) << 3 << -100 << QVector3D(2,2,2);
+}
 
-  QTest::newRow("(1.3, 1, 1) +1") << QVector3D(1.3, 1., 1.) << 1 << 1 << QVector3D(2,2,2);
-  QTest::newRow("(1.3, 1, 1) +0") << QVector3D(1.3, 1., 1.) << 1 << 0 << QVector3D(1,1,1);
-  QTest::newRow("(1.3, 1, 1) -1") << QVector3D(1.3, 1., 1.) << 1 << -1 << QVector3D(0,0,0);
 
-  QTest::newRow("(1.3, 1, 1.3) +1") << QVector3D(1.3, 1., 1.3) << 1 << 1 << QVector3D(2,2,2);
-  QTest::newRow("(1.3, 1, 1.3) +0") << QVector3D(1.3, 1., 1.3) << 1 << 0 << QVector3D(1,1,1);
-  QTest::newRow("(1.3, 1, 1.3) -1") << QVector3D(1.3, 1., 1.3) << 1 << -1 << QVector3D(0,1,1);
+// ----------------------------------------------------------------------------
+void ctkCoordinatesWidgetTester::testDecimalsByShortcuts()
+{
+  ctkCoordinatesWidget coordinatesWidget;
+  coordinatesWidget.setDecimalsOption(
+    ctkDoubleSpinBox::DecimalsByShortcuts);
+  coordinatesWidget.setDecimals(3);
+  coordinatesWidget.setCoordinates(1.1234567891011,1.1234567891011,1.1234567891011);
 
-  QTest::newRow("(1.*, 1, 1.3) +1") << QVector3D(1.12345678910121416, 1., 1.3) << 3 << 1 << QVector3D(4,4,4);
-  QTest::newRow("(1.*, 1, 1.3) +0") << QVector3D(1.12345678910121416, 1., 1.3) << 3 << 0 << QVector3D(3,3,3);
-  QTest::newRow("(1.*, 1, 1.3) -1") << QVector3D(1.12345678910121416, 1., 1.3) << 3 << -1 << QVector3D(2,2,2);
+  testDecimals(&coordinatesWidget, 3);
+  testDecimals(&coordinatesWidget, "1.123, 1.123, 1.123");
 
-  QTest::newRow("(1, 1.*, 1.3) +1") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 1 << QVector3D(4,4,4);
-  QTest::newRow("(1, 1.*, 1.3) +0") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 0 << QVector3D(3,3,3);
-  QTest::newRow("(1, 1.*, 1.3) -1") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << -1 << QVector3D(2,3,3);
+  // Simulate shortcut
+  QList<QDoubleSpinBox*> spinBoxes =
+    coordinatesWidget.findChildren<QDoubleSpinBox*>();
+  QFETCH(int, key);
+  QFETCH(int, repeat);
+  for (int i = 0; i < repeat; ++i)
+    {
+    QTest::keyClick(spinBoxes[0], key, Qt::ControlModifier);
+    }
+  QFETCH(int, expectedDecimals);
+  QFETCH(QString, expectedDisplayedText);
+  testDecimals(&coordinatesWidget, expectedDecimals);
+  testDecimals(&coordinatesWidget, expectedDisplayedText);
+}
 
-  QTest::newRow("(1.*, 1.*, 1.3) +1") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 1 << QVector3D(4,4,4);
-  QTest::newRow("(1.*, 1.*, 1.3) +0") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << 0 << QVector3D(3,3,3);
-  QTest::newRow("(1.*, 1.*, 1.3) -1") << QVector3D(1., 1.12345678910121416, 1.3) << 3 << -1 << QVector3D(2,3,3);
+// ----------------------------------------------------------------------------
+void ctkCoordinatesWidgetTester::testDecimalsByShortcuts_data()
+{
+  QTest::addColumn<int>("key");
+  QTest::addColumn<int>("repeat");
+  QTest::addColumn<int>("expectedDecimals");
+  QTest::addColumn<QString>("expectedDisplayedText");
+
+  QTest::newRow("+ -> 1.1235, 1.1235, 1.1235")
+    << static_cast<int>(Qt::Key_Plus) << 1 << 4 << "1.1235, 1.1235, 1.1235";
+  QTest::newRow("++ -> 1.12346, 1.12346, 1.12346")
+    << static_cast<int>(Qt::Key_Plus) << 2 << 5 << "1.12346, 1.12346, 1.12346";
+  QTest::newRow("- -> 1.12, 1.12, 1.12")
+    << static_cast<int>(Qt::Key_Minus) << 1 << 2 << "1.12, 1.12, 1.12";
+  QTest::newRow("-- -> 1.1, 1.1, 1.1")
+    << static_cast<int>(Qt::Key_Minus) << 2 << 1 << "1.1, 1.1, 1.1";
 }
 
 // ----------------------------------------------------------------------------

+ 2 - 2
Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetTest1.cpp

@@ -119,7 +119,7 @@ int ctkCoordinatesWidgetTest1(int argc, char * argv [] )
   coordinates = coordinatesWidget.coordinates();
   
   if (coordinates[0] != 1. ||
-      coordinates[1] != 10.0001 ||
+      coordinates[1] != 10.00012 ||
       coordinates[2] != -9999. ||
       coordinates[3] != 0.4)
     {
@@ -140,7 +140,7 @@ int ctkCoordinatesWidgetTest1(int argc, char * argv [] )
   
   coordinatesWidget.setCoordinatesAsString("1.000000001, -2, 3.01, 40000.01");
   coordinates = coordinatesWidget.coordinates();
-  if (!qFuzzyCompare(coordinates[0], 1.) ||
+  if (!qFuzzyCompare(coordinates[0], 1.000000001) ||
       !qFuzzyCompare(coordinates[1], -2.) ||
       !qFuzzyCompare(coordinates[2], 3.01) ||
       !qFuzzyCompare(coordinates[3], 9999.))

+ 206 - 115
Libs/Widgets/Testing/Cpp/ctkDoubleSpinBoxTest.cpp

@@ -46,6 +46,14 @@ private slots:
   void testSetValue();
   void testSetValue_data();
 
+  void testSetValueOutsideRange();
+
+  void testSetMinimum();
+  void testSetMinimum_data();
+
+  void testSetMaximum();
+  void testSetMaximum_data();
+
   void testSetRange();
   void testSetRange_data();
 
@@ -92,9 +100,9 @@ void ctkDoubleSpinBoxTester::testToLocals()
 {
   bool ok;
   QLocale().toDouble("+.0", &ok);
-  qDebug() << "+.0" << ok;
+  QVERIFY(ok);
   QLocale().toDouble("0.0 1", &ok);
-  qDebug() << "0.0 1" << ok;
+  QVERIFY(!ok);
 }
 
 //-----------------------------------------------------------------------------
@@ -102,26 +110,15 @@ void ctkDoubleSpinBoxTester::testSetValue()
 {
   ctkDoubleSpinBox spinBox;
   spinBox.setValue(25.);
-  // Compare with a QDoubleSpinBox as we are supposed to have the same behavior.
-  QDoubleSpinBox compareSpinBox;
-  compareSpinBox.setValue(25.);
 
   QFETCH(double, value);
-
-  QSignalSpy valueChangedSpy(&spinBox,
-                             SIGNAL(valueChanged(double)));
+  QSignalSpy valueChangedSpy(&spinBox, SIGNAL(valueChanged(double)));
   spinBox.setValue(value);
-  QSignalSpy compareValueChangedSpy(&compareSpinBox,
-                                    SIGNAL(valueChanged(double)));
-  compareSpinBox.setValue(value);
-
-  QCOMPARE(spinBox.value(), compareSpinBox.value());
-  QCOMPARE(valueChangedSpy.count(), compareValueChangedSpy.count());
 
   QFETCH(double, expectedValue);
   QCOMPARE(spinBox.value(), expectedValue);
 
-  const bool valueChanged = expectedValue != 25.;
+  const bool valueChanged = (expectedValue != 25.);
   QCOMPARE(valueChangedSpy.count(), valueChanged ? 1 : 0);
 }
 
@@ -132,14 +129,83 @@ void ctkDoubleSpinBoxTester::testSetValue_data()
   QTest::addColumn<double>("expectedValue");
 
   QTest::newRow("1. -> 1.]") << 1. << 1.;
+  QTest::newRow("25. -> 25.]") << 25. << 25.;
+  QTest::newRow("25.00001 -> 25.00001]") << 25.00001 << 25.00001;
   QTest::newRow("100. -> 99.99]") << 100. << 99.99;
   QTest::newRow("-1. -> 0.]") << -1. << 0.;
 
-  QTest::newRow("min -> 0.") << std::numeric_limits<double>::min() << 0.;
+  QTest::newRow("min -> 0.") << std::numeric_limits<double>::min() << std::numeric_limits<double>::min();
   QTest::newRow("max -> 99.99") << std::numeric_limits<double>::max() << 99.99;
   QTest::newRow("-inf -> 0.") << -std::numeric_limits<double>::infinity() << 0.;
   QTest::newRow("inf -> 99.99") << std::numeric_limits<double>::infinity() << 99.99;
-  QTest::newRow("NaN -> 99.99") << std::numeric_limits<double>::quiet_NaN() << 99.99;
+  QTest::newRow("NaN -> 99.99") << std::numeric_limits<double>::quiet_NaN() << 0.;
+}
+
+//-----------------------------------------------------------------------------
+void ctkDoubleSpinBoxTester::testSetValueOutsideRange()
+{
+  // This test is a bit different from testSetValue(), we start at 0. and must
+  // stay in 0.
+  ctkDoubleSpinBox spinBox;
+  QSignalSpy valueChangedSpy(&spinBox, SIGNAL(valueChanged(double)));
+  spinBox.setValue(-10.);
+  QCOMPARE(spinBox.value(), 0.);
+  QCOMPARE(valueChangedSpy.count(), 0);
+}
+
+// ----------------------------------------------------------------------------
+void ctkDoubleSpinBoxTester::testSetMinimum()
+{
+  ctkDoubleSpinBox spinBox;
+  QFETCH(double, minimum);
+  spinBox.setMinimum(minimum);
+
+  QFETCH(double, expectedMinimum);
+  QFETCH(double, expectedValue);
+
+  QCOMPARE(spinBox.minimum(), expectedMinimum);
+  QCOMPARE(spinBox.value(), expectedValue);
+}
+
+// ----------------------------------------------------------------------------
+void ctkDoubleSpinBoxTester::testSetMinimum_data()
+{
+  QTest::addColumn<double>("minimum");
+  QTest::addColumn<double>("expectedMinimum");
+  QTest::addColumn<double>("expectedValue");
+
+  QTest::newRow("0. -> 0.") << 0. << 0. << 0.;
+  QTest::newRow("99.99 -> 99.99") << 99.99 << 99.99 << 99.99;
+  QTest::newRow("10.0123 -> 10.0123") << 10.0123 << 10.0123 << 10.0123;
+  QTest::newRow("-10.0123 -> 0.") << -10.0123 << -10.0123 << 0.;
+  QTest::newRow("200.0123 -> 200.0123") << 200.0123 << 200.0123 << 200.0123;
+}
+
+// ----------------------------------------------------------------------------
+void ctkDoubleSpinBoxTester::testSetMaximum()
+{
+  ctkDoubleSpinBox spinBox;
+  QFETCH(double, maximum);
+  spinBox.setMaximum(maximum);
+
+  QFETCH(double, expectedMaximum);
+  QFETCH(double, expectedValue);
+  QCOMPARE(spinBox.maximum(), expectedMaximum);
+  QCOMPARE(spinBox.value(), expectedValue);
+}
+
+// ----------------------------------------------------------------------------
+void ctkDoubleSpinBoxTester::testSetMaximum_data()
+{
+  QTest::addColumn<double>("maximum");
+  QTest::addColumn<double>("expectedMaximum");
+  QTest::addColumn<double>("expectedValue");
+
+  QTest::newRow("0. -> 0.") << 0. << 0. << 0.;
+  QTest::newRow("99.99 -> 0.") << 99.99 << 99.99 << 0.;
+  QTest::newRow("10.0123 -> 0.") << 10.0123 << 10.0123 << 0.;
+  QTest::newRow("-10.0123 -> -10.0123") << -10.0123 << -10.0123 << -10.0123;
+  QTest::newRow("200.0123 -> 0.") << 200.0123 << 200.0123 << 0.;
 }
 
 //-----------------------------------------------------------------------------
@@ -147,22 +213,13 @@ void ctkDoubleSpinBoxTester::testSetRange()
 {
   ctkDoubleSpinBox spinBox;
   spinBox.setValue(25.);
-  QDoubleSpinBox compareSpinBox;
-  compareSpinBox.setValue(25.);
 
   QSignalSpy valueChangedSpy(&spinBox,
                              SIGNAL(valueChanged(double)));
-  QSignalSpy compareValueChangedSpy(&compareSpinBox,
-                             SIGNAL(valueChanged(double)));
 
   QFETCH(double, minimum);
   QFETCH(double, maximum);
   spinBox.setRange(minimum, maximum);
-  compareSpinBox.setRange(minimum, maximum);
-
-  ctkTest::COMPARE(spinBox.minimum(), compareSpinBox.minimum());
-  ctkTest::COMPARE(spinBox.maximum(), compareSpinBox.maximum());
-  ctkTest::COMPARE(spinBox.value(), compareSpinBox.value());
 
   QFETCH(double, expectedMinimum);
   QFETCH(double, expectedMaximum);
@@ -192,7 +249,7 @@ void ctkDoubleSpinBoxTester::testSetRange_data()
   QTest::newRow("[min,max]")
     << std::numeric_limits<double>::min()
     << std::numeric_limits<double>::max()
-    << 0. // rounded (see QDoubleSpinBox::setMinimum() doc)
+    << std::numeric_limits<double>::min()
     << QVariant(std::numeric_limits<double>::max()).toDouble()
     << 25.;
   QTest::newRow("[-max,max]")
@@ -238,10 +295,11 @@ void ctkDoubleSpinBoxTester::testDecimalsByKey()
   QTest::keyClick(spinBox.lineEdit(), static_cast<Qt::Key>(key));
 
   QFETCH(QString, expectedText);
+  QFETCH(double, expectedValue);
   QFETCH(int, expectedDecimals);
 
   QCOMPARE(spinBox.text(), expectedText);
-  QCOMPARE(spinBox.value(), expectedText.toDouble());
+  QCOMPARE(spinBox.value(), expectedValue);
   QCOMPARE(spinBox.decimals(), expectedDecimals);
   QCOMPARE(spy.count(), spinBox.decimals() != oldDecimals ? 1 : 0);
 }
@@ -253,126 +311,157 @@ void ctkDoubleSpinBoxTester::testDecimalsByKey_data()
   QTest::addColumn<int>("cursorPosition");
   QTest::addColumn<int>("key");
   QTest::addColumn<QString>("expectedText");
+  QTest::addColumn<double>("expectedValue");
   QTest::addColumn<int>("expectedDecimals");
 
   QList<int> options;
   // ctkDoubleSpinBox::DecimalsByKey
   options << ctkDoubleSpinBox::DecimalsByKey;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'1' -> 11.23") << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'del' -> .23") << options.last() << 0 << int(Qt::Key_Delete) << ".23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'backspace' -> 1.23") << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'1' -> 11.23") << options.last() << 1 << int(Qt::Key_1) << "11.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'del' -> 123") << options.last() << 1 << int(Qt::Key_Delete) << "123" << 0;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'backspace' -> .23") << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'1' -> 1.12") << options.last() << 2 << int(Qt::Key_1) << "1.12" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'del' -> 1.3") << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'backspace' -> 123") << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 0;
-
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'1' -> 1.21") << options.last() << 3 << int(Qt::Key_1) << "1.21" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'del' -> 1.2") << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'backspace' -> 1.3") << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1;
-
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'1' -> 1.231") << options.last() << 4 << int(Qt::Key_1) << "1.231" << 3;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'del' -> 1.23") << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'backspace' -> 1.2") << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'1' -> 11.23")
+    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 11.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'del' -> .23")
+    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << .23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 0:'backspace' -> 1.23")
+    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 1.23 << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'1' -> 11.23")
+    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 11.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'del' -> 123")
+    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 123. << 0;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 1:'backspace' -> .23")
+    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << .23 << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'1' -> 1.12")
+    << options.last() << 2 << int(Qt::Key_1) << "1.12" << 1.12 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'del' -> 1.3")
+    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1.3 << 1;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 2:'backspace' -> 123")
+    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 123. << 0;
+
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'1' -> 1.21")
+    << options.last() << 3 << int(Qt::Key_1) << "1.21" << 1.21 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'del' -> 1.2")
+    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1.2 << 1;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 3:'backspace' -> 1.3")
+    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1.3 << 1;
+
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'1' -> 1.231")
+    << options.last() << 4 << int(Qt::Key_1) << "1.231" << 1.231 << 3;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'del' -> 1.23")
+    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 1.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey 4:'backspace' -> 1.2")
+    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1.2 << 1;
 
   // ctkDoubleSpinBox::ReplaceDecimals
   options << ctkDoubleSpinBox::ReplaceDecimals;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'1' -> 11.23") << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'del' -> .23") << options.last() << 0 << int(Qt::Key_Delete) << ".23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'backspace' -> 1.23") << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'1' -> 11.23") << options.last() << 1 << int(Qt::Key_1) << "11.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'del' -> 123") << options.last() << 1 << int(Qt::Key_Delete) << "123" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'backspace' -> .23") << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'1' -> 1.13") << options.last() << 2 << int(Qt::Key_1) << "1.13" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'del' -> 1.3") << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'backspace' -> 123") << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'1' -> 1.21") << options.last() << 3 << int(Qt::Key_1) << "1.21" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'del' -> 1.2") << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'backspace' -> 1.3") << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 2;
-
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'1' -> 1.23") << options.last() << 4 << int(Qt::Key_1) << "1.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'del' -> 1.23") << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'backspace' -> 1.2") << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'1' -> 11.23")
+    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 11.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'del' -> .23")
+    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << .23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 0:'backspace' -> 1.23")
+    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 1.23 << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'1' -> 11.23")
+    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 11.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'del' -> 123")
+    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 123. << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 1:'backspace' -> .23")
+    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << .23 << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'1' -> 1.13")
+    << options.last() << 2 << int(Qt::Key_1) << "1.13" << 1.13 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'del' -> 1.3")
+    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1.3 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 2:'backspace' -> 123")
+    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 123. << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'1' -> 1.21")
+    << options.last() << 3 << int(Qt::Key_1) << "1.21" << 1.21 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'del' -> 1.2")
+    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1.2 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 3:'backspace' -> 1.3")
+    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1.3 << 2;
+
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'1' -> 1.23")
+    << options.last() << 4 << int(Qt::Key_1) << "1.23" << 1.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'del' -> 1.23")
+    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 1.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::ReplaceDecimals 4:'backspace' -> 1.2")
+    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1.2 << 2;
 
   // ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals
   options << (ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals);
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 0:'1' -> 11.23")
-    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 2;
+    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 11.23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 0:'del' -> .23")
-    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << 2;
+    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << .23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 0:'backspace' -> 1.23")
-    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 2;
+    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 1.23 << 2;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 1:'1' -> 11.23")
-    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 2;
+    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 11.23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 1:'del' -> 123")
-    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 0;
+    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 123. << 0;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 1:'backspace' -> .23")
-    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << 2;
+    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << .23 << 2;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 2:'1' -> 1.13")
-    << options.last() << 2 << int(Qt::Key_1) << "1.13" << 2;
+    << options.last() << 2 << int(Qt::Key_1) << "1.13" << 1.13 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 2:'del' -> 1.3")
-    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1;
+    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1.3 << 1;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 2:'backspace' -> 123")
-    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 0;
+    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 123. << 0;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 3:'1' -> 1.21")
-    << options.last() << 3 << int(Qt::Key_1) << "1.21" << 2;
+    << options.last() << 3 << int(Qt::Key_1) << "1.21" << 1.21 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 3:'del' -> 1.2")
-    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1;
+    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1.2 << 1;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 3:'backspace' -> 1.3")
-    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1;
+    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1.3 << 1;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 4:'1' -> 1.231")
-    << options.last() << 4 << int(Qt::Key_1) << "1.231" << 3;
+    << options.last() << 4 << int(Qt::Key_1) << "1.231" << 1.231 << 3;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 4:'del' -> 1.23")
-    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 4:'backspace' -> 1.23")
-    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1;
+    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 1.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::ReplaceDecimals 4:'backspace' -> 1.2")
+    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1.2 << 1;
 
   // ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals
   options << (ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals);
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 0:'1' -> 11.23")
-    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 2;
+    << options.last() << 0 << int(Qt::Key_1) << "11.23"<< 11.23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 0:'del' -> .23")
-    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << 2;
+    << options.last() << 0 << int(Qt::Key_Delete) << ".23" << .23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 0:'backspace' -> 1.23")
-    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 2;
+    << options.last() << 0 << int(Qt::Key_Backspace) << "1.23" << 1.23 << 2;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 1:'1' -> 11.23")
-    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 2;
+    << options.last() << 1 << int(Qt::Key_1) << "11.23" << 11.23 << 2;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 1:'del' -> 123")
-    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 0;
+    << options.last() << 1 << int(Qt::Key_Delete) << "123" << 123. << 0;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 1:'backspace' -> .23")
-    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << 2;
+    << options.last() << 1 << int(Qt::Key_Backspace) << ".23" << .23 << 2;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 2:'1' -> 1.123")
-    << options.last() << 2 << int(Qt::Key_1) << "1.123" << 3;
+    << options.last() << 2 << int(Qt::Key_1) << "1.123" << 1.123 << 3;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 2:'del' -> 1.3")
-    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1;
+    << options.last() << 2 << int(Qt::Key_Delete) << "1.3" << 1.3 << 1;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 2:'backspace' -> 123")
-    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 0;
+    << options.last() << 2 << int(Qt::Key_Backspace) << "123" << 123. << 0;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 3:'1' -> 1.213")
-    << options.last() << 3 << int(Qt::Key_1) << "1.213" << 3;
+    << options.last() << 3 << int(Qt::Key_1) << "1.213" << 1.213 << 3;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 3:'del' -> 1.2")
-    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1;
+    << options.last() << 3 << int(Qt::Key_Delete) << "1.2" << 1.2 << 1;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 3:'backspace' -> 1.3")
-    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1;
+    << options.last() << 3 << int(Qt::Key_Backspace) << "1.3" << 1.3 << 1;
 
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 4:'1' -> 1.231")
-    << options.last() << 4 << int(Qt::Key_1) << "1.231" << 3;
+    << options.last() << 4 << int(Qt::Key_1) << "1.231" << 1.231 << 3;
   QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 4:'del' -> 1.23")
-    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 2;
-  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 4:'backspace' -> 1.23")
-    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1;
+    << options.last() << 4 << int(Qt::Key_Delete) << "1.23" << 1.23 << 2;
+  QTest::newRow("ctkDoubleSpinBox::DecimalsByKey|ctkDoubleSpinBox::InsertDecimals 4:'backspace' -> 1.2")
+    << options.last() << 4 << int(Qt::Key_Backspace) << "1.2" << 1.2 << 1;
 
   foreach(int option, options)
     {
@@ -380,19 +469,19 @@ void ctkDoubleSpinBoxTester::testDecimalsByKey_data()
     for (int i = 0; i < 5; ++i)
       {
       QTest::newRow(QString("%1 %2:'a' -> 1.23").arg(option).arg(i).toLatin1())
-        << option << i << int(Qt::Key_A) << "1.23" << 2;
+        << option << i << int(Qt::Key_A) << "1.23" << 1.23 << 2;
       }
     // sign keys are only for the first digit
     QTest::newRow(QString("%1 0:'+' -> 1.23").arg(option).toLatin1())
-      << option << 0 << int(Qt::Key_Plus) << "+1.23" << 2;
+      << option << 0 << int(Qt::Key_Plus) << "+1.23" << 1.23 << 2;
     QTest::newRow(QString("%1 0:'-' -> -1.23").arg(option).toLatin1())
-      << option << 0 << int(Qt::Key_Minus) << "-1.23" << 2;
+      << option << 0 << int(Qt::Key_Minus) << "-1.23" << -1.23 << 2;
     for (int i = 1; i < 5; ++i)
       {
       QTest::newRow(QString("%1 %2:'+' -> 1.23").arg(option).arg(i).toLatin1())
-        << option << i << int(Qt::Key_Plus) << "1.23" << 2;
+        << option << i << int(Qt::Key_Plus) << "1.23" << 1.23 << 2;
       QTest::newRow(QString("%1 %2:'-' -> 1.23").arg(option).arg(i).toLatin1())
-        << option << i << int(Qt::Key_Minus) << "1.23" << 2;
+        << option << i << int(Qt::Key_Minus) << "1.23" << 1.23 << 2;
       }
     }
 }
@@ -456,10 +545,11 @@ void ctkDoubleSpinBoxTester::testDecimalsByValue()
   spinBox.setValue(value);
 
   QFETCH(QString, expectedText);
+  QFETCH(double, expectedValue);
   QFETCH(int, expectedDecimals);
 
   QCOMPARE(spinBox.text(), expectedText);
-  QCOMPARE(spinBox.value(), expectedText.toDouble());
+  QCOMPARE(spinBox.value(), expectedValue);
   QCOMPARE(spinBox.decimals(), expectedDecimals);
   QCOMPARE(spy.count(), spinBox.decimals() != oldDecimals ? 1 : 0);
 }
@@ -469,20 +559,21 @@ void ctkDoubleSpinBoxTester::testDecimalsByValue_data()
 {
   QTest::addColumn<double>("value");
   QTest::addColumn<QString>("expectedText");
+  QTest::addColumn<double>("expectedValue");
   QTest::addColumn<int>("expectedDecimals");
 
-  QTest::newRow("0") << 0. << "0"<< 0;
-  QTest::newRow("0.1") << 0.1 << "0.1" << 1;
-  QTest::newRow("0.02") << 0.02 << "0.02" << 2;
-  QTest::newRow("10.003") << 10.003 << "10.003" << 3;
-  QTest::newRow("-0.0004") << -0.0004 << "-0.0004" << 4;
-  QTest::newRow("0.000056") << 0.000056 << "0.000056" << 6;
+  QTest::newRow("0") << 0. << "0"<< 0. << 0;
+  QTest::newRow("0.1") << 0.1 << "0.1" << 0.1 << 1;
+  QTest::newRow("0.02") << 0.02 << "0.02" << 0.02 << 2;
+  QTest::newRow("10.003") << 10.003 << "10.003" << 10.003 << 3;
+  QTest::newRow("-0.0004") << -0.0004 << "-0.0004" << -0.0004 << 4;
+  QTest::newRow("0.000056") << 0.000056 << "0.000056" << 0.000056 << 6;
   // internally represented as 123456.001109999997425
-  QTest::newRow("5.00111") << 5.00111 << "5.00111" << 5;
-  QTest::newRow("same value with more decimals") << 1.234567 << "1.234567" << 6;
-  QTest::newRow("same value") << 1.23 << "1.23" << 2;
-  QTest::newRow("same value with less decimals") << 1.234 << "1.234" << 3;
-  QTest::newRow("16 decimals") << 0.1234567891013151 << "0.1235" << 4;
+  QTest::newRow("5.00111") << 5.00111 << "5.00111" << 5.00111 << 5;
+  QTest::newRow("same value with more decimals") << 1.234567 << "1.234567" << 1.234567 << 6;
+  QTest::newRow("same value") << 1.23 << "1.23" << 1.23 << 2;
+  QTest::newRow("same value with less decimals") << 1.234 << "1.234" << 1.234 << 3;
+  QTest::newRow("16 decimals") << 0.1234567891013151 << "0.1235" << 0.1234567891013151 << 4;
 }
 
 // ----------------------------------------------------------------------------

+ 5 - 5
Libs/Widgets/Testing/Cpp/ctkDoubleSpinBoxTest1.cpp

@@ -168,7 +168,7 @@ int ctkDoubleSpinBoxTest1(int argc, char * argv [] )
     }
 
   spinBox.setValue(28.366917352);
-  if (!qFuzzyCompare(spinBox.value(), 28.367))
+  if (!qFuzzyCompare(spinBox.value(), 28.366917352))
     {
     qDebug()<<"setValue does not correspond. Got: "<<spinBox.value() ;
     return EXIT_FAILURE;
@@ -176,26 +176,26 @@ int ctkDoubleSpinBoxTest1(int argc, char * argv [] )
 
   qDebug()<<"SetValueIfDifferent:";
   spinBox.setValueIfDifferent(33.312587);
-  if (!qFuzzyCompare(spinBox.value(), 33.313))
+  if (!qFuzzyCompare(spinBox.value(), 33.312587))
     {
     qDebug()<<"setValueIfDifferent does not correspond. Got: "<<spinBox.value() ;
     return EXIT_FAILURE;
     }
 
   spinBox.setValueIfDifferent(33.312960134);
-  if (!qFuzzyCompare(spinBox.value(), 33.313))
+  if (!qFuzzyCompare(spinBox.value(), 33.312960134))
     {
     qDebug()<<"setValueIfDifferent does not correspond. Got: "<<spinBox.value() ;
     return EXIT_FAILURE;
     }
 
-  if (spy.count() != 3)
+  if (spy.count() != 4)
     {
     qDebug()<<"spy got wrong number of signal sent : "<<spy.count() ;
     return EXIT_FAILURE;
     }
 
-//-----------------------------------------------------------------------------
+  //-----------------------------------------------------------------------------
   spinBox.show();
 
   if (argc < 2 || QString(argv[1]) != "-I" )

+ 5 - 5
Libs/Widgets/Testing/Cpp/ctkDoubleSpinBoxValueProxyTest.cpp

@@ -114,10 +114,10 @@ void ctkDoubleSpinBoxValueProxyTester::testSetValue_data()
   // Offset
   QTest::newRow("Offset only") << 1. << 42.19 << 0.1 << 0.1 << "0.10";
   // \tbd could be improved ?
-  QTest::newRow("Precision descrepancy 3")
-    << 1. << 42.197 << 0.1 << 0.103 << "0.10";
-  QTest::newRow("Precision descrepancy 4")
-    << 1. << 42.1971 << 0.1 << 0.1029 << "0.10";
+  QTest::newRow("Offset only: keep precision 3")
+    << 1. << 42.197 << 0.1 << 0.1 << "0.10";
+  QTest::newRow("Offset only: keep precision 4")
+    << 1. << 42.1971 << 0.1 << 0.1 << "0.10";
 
   QTest::newRow("Offset only: less than min")
     << 1. << -42.19 << -220.0 << -200. << "-200.00";
@@ -266,7 +266,7 @@ void ctkDoubleSpinBoxValueProxyTester::testSetCoefficient_data()
   QTest::newRow("100") << 100.0 << 10.12 << 1012.;
   QTest::newRow("10") << 10.0 << 10.12 << 101.2;
   QTest::newRow("1") << 1.0 << 10.12 << 10.12;
-  QTest::newRow("0.10") << 0.1 << 10.1 << 1.01;
+  QTest::newRow("0.10") << 0.1 << 10.12 << 1.01;
   QTest::newRow("-10") << -10.0 << 10.12 << -101.2;
 }
 

+ 13 - 7
Libs/Widgets/Testing/Cpp/ctkRangeWidgetTest.cpp

@@ -93,11 +93,12 @@ void ctkRangeWidgetTester::testSetMinimumValue_data()
   QTest::newRow("100. -> 75.]") << 100. << 75.;
   QTest::newRow("-1. -> 0.]") << -1. << 0.;
 
-  QTest::newRow("min -> 0.") << std::numeric_limits<double>::min() << 0.;
+  QTest::newRow("min -> min") << std::numeric_limits<double>::min()
+                              << std::numeric_limits<double>::min();
   QTest::newRow("max -> 75.") << std::numeric_limits<double>::max() << 75.;
   QTest::newRow("-inf -> 0.") << -std::numeric_limits<double>::infinity() << 0.;
   QTest::newRow("inf -> 75.") << std::numeric_limits<double>::infinity() << 75.;
-  QTest::newRow("NaN -> 75.") << std::numeric_limits<double>::quiet_NaN() << 75.;
+  QTest::newRow("NaN -> 75.") << std::numeric_limits<double>::quiet_NaN() << 0.;
 }
 
 
@@ -137,7 +138,7 @@ void ctkRangeWidgetTester::testSetMaximumValue_data()
   QTest::newRow("max -> 99.") << std::numeric_limits<double>::max() << 99.;
   QTest::newRow("-inf -> 25.") << -std::numeric_limits<double>::infinity() << 25.;
   QTest::newRow("inf -> 99.") << std::numeric_limits<double>::infinity() << 99.;
-  QTest::newRow("NaN -> 99.") << std::numeric_limits<double>::quiet_NaN() << 99.;
+  QTest::newRow("NaN -> 99.") << std::numeric_limits<double>::quiet_NaN() << 25.;
 }
 
 //-----------------------------------------------------------------------------
@@ -185,15 +186,16 @@ void ctkRangeWidgetTester::testSetValues_data()
   QTest::newRow("[10.,1.] -> [1., 10.]") << 10. << 1. << 1. << 10.;
   QTest::newRow("[-1.,100.] -> [0., 99.]") << -1. << 100. << 0. << 99.;
 
-  QTest::newRow("[min,max] -> [0., 99.]")
+  QTest::newRow("[min,max] -> [min, 99.]")
     << std::numeric_limits<double>::min()
-    << std::numeric_limits<double>::max() << 0. << 99.;
+    << std::numeric_limits<double>::max()
+    << std::numeric_limits<double>::min() << 99.;
   QTest::newRow("[-inf,inf] -> [0., 99.]")
     << -std::numeric_limits<double>::infinity()
     << std::numeric_limits<double>::infinity() << 0. << 99.;
   QTest::newRow("[NaN,NaN] -> [99., 99.]")
     << std::numeric_limits<double>::quiet_NaN()
-    << std::numeric_limits<double>::quiet_NaN() << 99. << 99.;
+    << std::numeric_limits<double>::quiet_NaN() << 0. << 0.;
 }
 
 
@@ -245,15 +247,19 @@ void ctkRangeWidgetTester::testSetRange_data()
   QTest::addColumn<double>("expectedUpperValue");
 
   QTest::newRow("[1.,98.]") << 1. << 98. << 1. << 98. << 25. << 75.;
+  QTest::newRow("[10.0123,99.99]") << 10.0123 << 99.99 << 10.0123 << 99.99 << 25. << 75.;
+  QTest::newRow("[0.,2050.9876.]") << 0. << 2050.9876 << 0. << 2050.9876 << 25. << 75.;
   QTest::newRow("[-1.,101.]") << -1. << 101. << -1. << 101. << 25. << 75.;
   QTest::newRow("[1.,50.]") << 1. << 50. << 1. << 50. << 25. << 50.;
+  QTest::newRow("[1., 50.5678]") << 1. << 50.5678 << 1. << 50.5678 << 25. << 50.5678 ;
   QTest::newRow("[50.,99.]") << 50. << 99. << 50. << 99. << 50. << 75. ;
+  QTest::newRow("[50.5678,99.]") << 50.5678 << 99. << 50.5678 << 99. << 50.5678 << 75. ;
   QTest::newRow("[1.,10.]") << 1. << 10. << 1. << 10. << 10. << 10.;
   QTest::newRow("[90.,99.]") << 90. << 99. << 90. << 99. << 90. << 90.;
   QTest::newRow("[min,max]")
     << std::numeric_limits<double>::min()
     << std::numeric_limits<double>::max()
-    << 0.
+    << std::numeric_limits<double>::min()
     << std::numeric_limits<double>::max()
     << 25. << 75.;
   QTest::newRow("[-inf,inf]")

+ 33 - 137
Libs/Widgets/Testing/Cpp/ctkRangeWidgetTest1.cpp

@@ -30,14 +30,6 @@
 #include <iostream>
 
 //-----------------------------------------------------------------------------
-bool checkSlider(const ctkRangeWidget& slider)
-{
-  return slider.minimum()  <= slider.minimumValue() &&
-    slider.minimumValue() <= slider.maximumValue() &&
-    slider.maximumValue() <= slider.maximum();
-}
-
-//-----------------------------------------------------------------------------
 bool checkSlider(const ctkRangeWidget& slider,
                  double min, double minVal, double maxVal, double max)
 {
@@ -46,126 +38,42 @@ bool checkSlider(const ctkRangeWidget& slider,
     qFuzzyCompare(slider.maximumValue(), maxVal) &&
     qFuzzyCompare(slider.maximum(), max);
 }
+
 //-----------------------------------------------------------------------------
 int ctkRangeWidgetTest1(int argc, char * argv [] )
 {
   QApplication app(argc, argv);
 
   ctkRangeWidget sliderSpinBox;
-
-  if (!checkSlider(sliderSpinBox))
-    {
-    std::cerr << "ctkRangeSlider:: 0) "
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-  // the first part of the tests infer 2 decimals
-  if (sliderSpinBox.decimals() != 2)
-    {
-    std::cerr << "ctkRangeWidget::decimals default value failed."
-              << sliderSpinBox.decimals() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setValues(32.12,75.38);
-  if (!checkSlider(sliderSpinBox, 0., 32.12, 75.38, 99.))
-    {
-    std::cerr << "ctkRangeWidget:: 1) setValues"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setMinimum(10.0123);
-
-  if (!checkSlider(sliderSpinBox, 10.01, 32.12, 75.38, 99.))
-    {
-    std::cerr << "ctkRangeWidget:: 2) setMinimum "
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-  
   sliderSpinBox.setMaximum(2050.9876);
-
-  if (!checkSlider(sliderSpinBox, 10.01, 32.12, 75.38, 2050.99))
-    {
-    std::cerr << "ctkRangeWidget:: 3) setMaximum "
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-  std::cout << "1" << std::endl;
   sliderSpinBox.setSingleStep(0.1);
-
-  if (!qFuzzyCompare(sliderSpinBox.singleStep(), 0.1) || 
-      !checkSlider(sliderSpinBox, 10.01, 32.12, 75.38, 2050.99))
-    {
-    std::cerr << "ctkRangeWidget:: 4) SetSingleStep"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-  std::cout << "2" << std::endl;
-  sliderSpinBox.setDecimals(1);
-
-  if (sliderSpinBox.decimals() != 1 || 
-      !checkSlider(sliderSpinBox, 10.0, 32.1, 75.4, 2051.0))
-    {
-    std::cerr << "ctkRangeWidget:: 5) SetDecimals"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-
-  std::cout << "3" << std::endl;
   sliderSpinBox.setMaximumValue(77.777);
+  sliderSpinBox.setMinimum(80.5678);
 
-  if (!checkSlider(sliderSpinBox, 10.0, 32.1, 77.8, 2051.0))
-    {
-    std::cerr << "ctkRangeWidget:: 6) SetMaximumValue"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
   std::cout << "4" << std::endl;
   sliderSpinBox.setTickInterval(0.1);
 
-  if (!qFuzzyCompare(sliderSpinBox.tickInterval(), 0.1) || 
-      !checkSlider(sliderSpinBox, 10.0, 32.1, 77.8, 2051.0))
+  if (!qFuzzyCompare(sliderSpinBox.tickInterval(), 0.1) ||
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 6) setTickInterval"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
     std::cout << "5" << std::endl;
-  sliderSpinBox.setMinimum(80.5678);
 
-  if (!qFuzzyCompare(sliderSpinBox.tickInterval(), 0.1) || 
-      !checkSlider(sliderSpinBox, 80.6, 80.6, 80.6, 2051.0))
+  if (!qFuzzyCompare(sliderSpinBox.tickInterval(), 0.1) |
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 6) setMinimum"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
@@ -174,13 +82,13 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
   //sliderSpinBox.reset();
   sliderSpinBox.setSpinBoxAlignment(Qt::AlignRight);
 
-  if (sliderSpinBox.spinBoxAlignment() != Qt::AlignRight || 
-      !checkSlider(sliderSpinBox, 80.6, 80.6, 80.6, 2051.0))
+  if (sliderSpinBox.spinBoxAlignment() != Qt::AlignRight ||
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 7) setSpinBoxAlignment"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
@@ -188,13 +96,13 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
   std::cout << "7" << std::endl;
   sliderSpinBox.setAutoSpinBoxWidth(false);
 
-  if (sliderSpinBox.isAutoSpinBoxWidth() != false || 
-      !checkSlider(sliderSpinBox, 80.6, 80.6, 80.6, 2051.0))
+  if (sliderSpinBox.isAutoSpinBoxWidth() != false ||
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 8) setAutoSpinBoxWidth"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
@@ -202,44 +110,32 @@ int ctkRangeWidgetTest1(int argc, char * argv [] )
   std::cout << "8" << std::endl;
   sliderSpinBox.setPrefix("$");
 
-  if (sliderSpinBox.prefix() != "$" || 
-      !checkSlider(sliderSpinBox, 80.6, 80.6, 80.6, 2051.0))
+  if (sliderSpinBox.prefix() != "$" ||
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 8) setPrefix"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
   std::cout << "9" << std::endl;
   sliderSpinBox.setSuffix("mm");
 
-  if (sliderSpinBox.suffix() != "mm" || 
-      !checkSlider(sliderSpinBox, 80.6, 80.6, 80.6, 2051.0))
+  if (sliderSpinBox.suffix() != "mm" ||
+      !checkSlider(sliderSpinBox, 80.5678, 80.5678, 80.5678, 2050.9876))
     {
     std::cerr << "ctkRangeWidget:: 9) setSuffix"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
-  std::cout << "10" << std::endl;
-  sliderSpinBox.setDecimals(0);
-
-  if (!checkSlider(sliderSpinBox, 81., 81., 81., 2051.))
-    {
-    std::cerr << "ctkRangeWidget:: 10) setDecimals"
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.minimumValue() << " " 
-              << sliderSpinBox.maximumValue() << " " 
+              << sliderSpinBox.minimum() << " "
+              << sliderSpinBox.minimumValue() << " "
+              << sliderSpinBox.maximumValue() << " "
               << sliderSpinBox.maximum() << std::endl;
     return EXIT_FAILURE;
     }
 
   sliderSpinBox.setSymmetricMoves(true);
-  
+
   if (sliderSpinBox.symmetricMoves() != true)
     {
     std::cerr << "ctkRangeWidget::setSymmetricMoves failed" << std::endl;

+ 60 - 0
Libs/Widgets/Testing/Cpp/ctkSliderWidgetTest.cpp

@@ -40,6 +40,12 @@ class ctkSliderWidgetTester: public QObject
 private slots:
   void testUI();
 
+  void testSetMinimum();
+  void testSetMinimum_data();
+
+  void testSetMaximum();
+  void testSetMaximum_data();
+
   void testDecimalsByValue();
   /// This test makes sure the number of decimals increased with Ctrl+'+' does
   /// not break the synchronization between the value of the slider and the
@@ -66,6 +72,60 @@ void ctkSliderWidgetTester::testUI()
 }
 
 // ----------------------------------------------------------------------------
+void ctkSliderWidgetTester::testSetMinimum()
+{
+  ctkSliderWidget slider;
+  QFETCH(double, minimum);
+  slider.setMinimum(minimum);
+
+  QFETCH(double, expectedMinimum);
+  QFETCH(double, expectedValue);
+
+  QCOMPARE(slider.minimum(), expectedMinimum);
+  QCOMPARE(slider.value(), expectedValue);
+}
+
+// ----------------------------------------------------------------------------
+void ctkSliderWidgetTester::testSetMinimum_data()
+{
+  QTest::addColumn<double>("minimum");
+  QTest::addColumn<double>("expectedMinimum");
+  QTest::addColumn<double>("expectedValue");
+
+  QTest::newRow("0. -> 0.]") << 0. << 0. << 0.;
+  QTest::newRow("10.0123 -> 10.0123]") << 10.0123 << 10.0123 << 10.0123;
+  QTest::newRow("-10.0123 -> -10.0123]") << -10.0123 << -10.0123 << 0.;
+  QTest::newRow("200.0123 -> 200.0123]") << 200.0123 << 200.0123 << 200.0123;
+}
+
+// ----------------------------------------------------------------------------
+void ctkSliderWidgetTester::testSetMaximum()
+{
+  ctkSliderWidget slider;
+  QFETCH(double, maximum);
+  slider.setMaximum(maximum);
+
+  QFETCH(double, expectedMaximum);
+  QFETCH(double, expectedValue);
+  QCOMPARE(slider.maximum(), expectedMaximum);
+  QCOMPARE(slider.value(), expectedValue);
+
+}
+
+// ----------------------------------------------------------------------------
+void ctkSliderWidgetTester::testSetMaximum_data()
+{
+  QTest::addColumn<double>("maximum");
+  QTest::addColumn<double>("expectedMaximum");
+  QTest::addColumn<double>("expectedValue");
+
+  QTest::newRow("0. -> 0.") << 0. << 0. << 0.;
+  QTest::newRow("10.0123 -> 0.") << 10.0123 << 10.0123 << 0.;
+  QTest::newRow("-10.0123 -> -10.0123") << -10.0123 << -10.0123 << -10.0123;
+  QTest::newRow("200.0123 -> 0.") << 200.0123 << 200.0123 << 0.;
+}
+
+// ----------------------------------------------------------------------------
 void ctkSliderWidgetTester::testDecimalsByValue()
 {
   ctkSliderWidget slider;

+ 5 - 114
Libs/Widgets/Testing/Cpp/ctkSliderWidgetTest1.cpp

@@ -35,116 +35,19 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
   QApplication app(argc, argv);
 
   ctkSliderWidget sliderSpinBox;
-
-  // the first part of the tests infer 2 decimals
-  if (sliderSpinBox.decimals() != 2)
-    {
-    std::cerr << "ctkSliderWidget::decimals default value failed."
-              << sliderSpinBox.decimals() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setValue(32.12);
-
-  if (!qFuzzyCompare(sliderSpinBox.value(), 32.12))
-    {
-    std::cerr << "ctkSliderWidget::setValue failed."
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setMinimum(10.0123);
-
-  if (!qFuzzyCompare(sliderSpinBox.minimum(), 10.01) || 
-      !qFuzzyCompare(sliderSpinBox.value(), 32.12))
-    {
-    std::cerr << "ctkSliderWidget::setMinimum failed."
-              << sliderSpinBox.minimum() << " "
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-  
   sliderSpinBox.setMaximum(2050.9876);
-
-  if (!qFuzzyCompare(sliderSpinBox.maximum(), 2050.99) || 
-      !qFuzzyCompare(sliderSpinBox.value(), 32.12))
-    {
-    std::cerr << "ctkSliderWidget::setMaximum failed."
-              << sliderSpinBox.maximum() << " "
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setSingleStep(0.1);
-
-  if (!qFuzzyCompare(sliderSpinBox.singleStep(), 0.1) || 
-      !qFuzzyCompare(sliderSpinBox.value(), 32.12))
-    {
-    std::cerr << "ctkSliderWidget::setSingleStep failed."
-              << sliderSpinBox.singleStep() << " "
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.setDecimals(1);
-
-  if (sliderSpinBox.decimals() != 1 || 
-      !qFuzzyCompare(sliderSpinBox.value(), 32.1) ||
-      !qFuzzyCompare(sliderSpinBox.minimum(), 10.0) ||
-      !qFuzzyCompare(sliderSpinBox.maximum(), 2051.0))
-    {
-    std::cerr << "ctkSliderWidget::setDecimals failed."
-              << sliderSpinBox.decimals() << " "
-              << sliderSpinBox.value() << " " 
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }  
-
   sliderSpinBox.setValue(77.777);
-
-  if (!qFuzzyCompare(sliderSpinBox.value(), 77.8))
-    {
-    std::cerr << "ctkSliderWidget::setValue failed."
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }  
-
   sliderSpinBox.setTickInterval(0.1);
-
-  if (!qFuzzyCompare(sliderSpinBox.tickInterval(), 0.1) || 
-      !qFuzzyCompare(sliderSpinBox.value(), 77.8))
-    {
-    std::cerr << "ctkSliderWidget::setTickInterval failed."
-              << sliderSpinBox.tickInterval() << " "
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-  
   sliderSpinBox.setMinimum(80.5678);
-
-  if (!qFuzzyCompare(sliderSpinBox.minimum(), 80.6) || 
-      !qFuzzyCompare(sliderSpinBox.value(), 80.6))
-    {
-    std::cerr << "ctkSliderWidget::setMinimum failed."
-              << sliderSpinBox.minimum() << " "
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-
   sliderSpinBox.reset();
-
-  if (!qFuzzyCompare(sliderSpinBox.value(), 80.6))
-    {
-    std::cerr << "ctkSliderWidget::reset failed."
-              << sliderSpinBox.value() << std::endl;
-    return EXIT_FAILURE;
-    }
-  
   sliderSpinBox.setSpinBoxAlignment(Qt::AlignRight);
 
   if (sliderSpinBox.spinBoxAlignment() != Qt::AlignRight || 
-      !qFuzzyCompare(sliderSpinBox.value(), 80.6))
+      !qFuzzyCompare(sliderSpinBox.value(), 80.5678))
     {
     std::cerr << "ctkSliderWidget::setSpinBoxAlignment failed."
               << sliderSpinBox.spinBoxAlignment() << " "
@@ -156,7 +59,7 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
 
   if (sliderSpinBox.synchronizeSiblings().testFlag(
       ctkSliderWidget::SynchronizeWidth) != false ||
-      !qFuzzyCompare(sliderSpinBox.value(), 80.6))
+      !qFuzzyCompare(sliderSpinBox.value(), 80.5678))
     {
     std::cerr << "ctkSliderWidget::setAutoSpinBoxWidth failed."
               << sliderSpinBox.synchronizeSiblings() << " "
@@ -167,7 +70,7 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
   sliderSpinBox.setPrefix("$");
 
   if (sliderSpinBox.prefix() != "$" || 
-      !qFuzzyCompare(sliderSpinBox.value(), 80.6))
+      !qFuzzyCompare(sliderSpinBox.value(), 80.5678))
     {
     std::cerr << "ctkSliderWidget::setPrefix failed."
               << sliderSpinBox.prefix().toLatin1().data() << " "
@@ -178,7 +81,7 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
   sliderSpinBox.setSuffix("mm");
 
   if (sliderSpinBox.suffix() != "mm" || 
-      !qFuzzyCompare(sliderSpinBox.value(), 80.6))
+      !qFuzzyCompare(sliderSpinBox.value(), 80.5678))
     {
     std::cerr << "ctkSliderWidget::setSuffix failed."
               << sliderSpinBox.suffix().toLatin1().data() << " "
@@ -186,18 +89,6 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
     return EXIT_FAILURE;
     }
 
-  sliderSpinBox.setDecimals(0);
-
-  if (!qFuzzyCompare(sliderSpinBox.value(), 81) ||
-      !qFuzzyCompare(sliderSpinBox.minimum(), 81) ||
-      !qFuzzyCompare(sliderSpinBox.maximum(), 2051))
-    {
-    std::cerr << "ctkSliderWidget::setDecimals failed."
-              << sliderSpinBox.value() << " " 
-              << sliderSpinBox.minimum() << " " 
-              << sliderSpinBox.maximum() << std::endl;
-    return EXIT_FAILURE;
-    }
   sliderSpinBox.setPageStep(1.);
   if (sliderSpinBox.pageStep() != 1.)
     {
@@ -205,7 +96,7 @@ int ctkSliderWidgetTest1(int argc, char * argv [] )
               << " val: " << sliderSpinBox.pageStep() << std::endl;
     return EXIT_FAILURE;
     }
-  // FIXME check that the correct signals are sent.
+
   sliderSpinBox.show();
   if (argc < 2 || QString(argv[1]) != "-I" )
     {

+ 1 - 1
Libs/Widgets/Testing/Cpp/ctkSliderWidgetValueProxyTest.cpp

@@ -102,7 +102,7 @@ void ctkSliderWidgetValueProxyTester::testSetValue_data()
   //---------------------------------------------------------------------------
   // Offset
   QTest::newRow("Offset only") << 1.0 << 42.19 << 0.1 << 0.1;
-  QTest::newRow("Offset only: lost precision") << 1.0 << 42.19176 << 0.1 << 0.09824;
+  QTest::newRow("Offset only: keep precision") << 1.0 << 42.19176 << 0.1 << 0.1;
 
   QTest::newRow("Offset only: less than min")
     << 1.0 << 42.19 << -510.0 << -200.;

+ 60 - 7
Libs/Widgets/ctkCoordinatesWidget.cpp

@@ -26,6 +26,7 @@
 // CTK includes
 #include "ctkCoordinatesWidget.h"
 #include "ctkDoubleSpinBox.h"
+#include "ctkDoubleSpinBox_p.h"
 #include "ctkUtils.h"
 #include "ctkValueProxy.h"
 
@@ -209,7 +210,22 @@ void ctkCoordinatesWidget::updateDecimals()
   int maxDecimals = 0;
   for (int i = 0; i < this->Dimension; ++i)
     {
-    maxDecimals = qMax(maxDecimals, this->spinBox(i)->decimals());
+    int spinBoxDecimals = this->Decimals;
+    if (this->decimalsOption() & ctkDoubleSpinBox::DecimalsByKey ||
+        this->decimalsOption() & ctkDoubleSpinBox::DecimalsByShortcuts)
+      {
+      spinBoxDecimals = this->spinBox(i)->decimals();
+      }
+    if (this->decimalsOption() & ctkDoubleSpinBox::DecimalsByValue)
+      {
+      spinBoxDecimals = ctkCoordinatesWidget::spinBoxSignificantDecimals(
+        this->spinBox(i));
+      if (spinBoxDecimals == 16)
+        {
+        spinBoxDecimals = this->spinBox(i)->decimals();
+        }
+      }
+    maxDecimals = qMax(maxDecimals, spinBoxDecimals);
     }
   this->ChangingDecimals = true;
   this->setTemporaryDecimals(maxDecimals);
@@ -217,24 +233,40 @@ void ctkCoordinatesWidget::updateDecimals()
 }
 
 //------------------------------------------------------------------------------
-void ctkCoordinatesWidget::updateOtherDecimals(int decimals)
+void ctkCoordinatesWidget::updateOtherDecimals(int senderDecimals)
 {
   if (this->ChangingDecimals)
     {
     return;
     }
-  int maxDecimals = decimals;
+  int senderSpinBoxDecimals = ctkCoordinatesWidget::spinBoxSignificantDecimals(
+    qobject_cast<ctkDoubleSpinBox*>(this->sender()));
+
+  int maxDecimals = senderDecimals;
   for (int i = 0; i < this->Dimension; ++i)
     {
     if (this->sender() == this->spinBox(i))
       {
       continue;
       }
-    int spinBoxDecimals = this->spinBox(i)->decimals();
+    int spinBoxDecimals = maxDecimals;
+    if (this->decimalsOption() & ctkDoubleSpinBox::DecimalsByKey)
+      {
+      spinBoxDecimals = this->spinBox(i)->decimals();
+      }
     if (this->decimalsOption() & ctkDoubleSpinBox::DecimalsByValue)
       {
-      spinBoxDecimals = ctk::significantDecimals(
-        this->spinBox(i)->displayedValue(), maxDecimals);
+      spinBoxDecimals = ctkCoordinatesWidget::spinBoxSignificantDecimals(
+        this->spinBox(i));
+      // if the edited spinbox has an undefined number of decimals and the
+      // the current spinbox too, then use the new number of decimals otherwise
+      // there would be no way to increase/decrease decimals for all the
+      // spinboxes.
+      if (spinBoxDecimals == 16)
+        {
+        spinBoxDecimals = (senderSpinBoxDecimals == 16)?
+          senderDecimals : this->spinBox(i)->decimals();
+        }
       }
     maxDecimals = qMax(maxDecimals, spinBoxDecimals);
     }
@@ -252,8 +284,29 @@ void ctkCoordinatesWidget::setTemporaryDecimals(int newDecimals)
       {
       continue;
       }
-    this->spinBox(i)->spinBox()->setDecimals(newDecimals);
+    // Increasing the number of decimals might have lost precision.
+    double currentValue = this->spinBox(i)->value();
+    if (this->spinBox(i)->valueProxy())
+      {
+      currentValue = this->spinBox(i)->valueProxy()->proxyValueFromValue(currentValue);
+      }
+    this->spinBox(i)->d_ptr->setValue(currentValue, newDecimals);
+    }
+}
+
+//------------------------------------------------------------------------------
+int ctkCoordinatesWidget::spinBoxSignificantDecimals(ctkDoubleSpinBox* spinBox)
+{
+  if (!spinBox)
+    {
+    return 0;
+    }
+  double currentValue = spinBox->value();
+  if (spinBox->valueProxy())
+    {
+    currentValue = spinBox->valueProxy()->proxyValueFromValue(currentValue);
     }
+  return ctk::significantDecimals(currentValue);
 }
 
 //------------------------------------------------------------------------------

+ 5 - 0
Libs/Widgets/ctkCoordinatesWidget.h

@@ -158,6 +158,11 @@ protected:
   static double norm(double* coordinates, int dimension);
   static double squaredNorm(double* coordinates, int dimension);
 
+  /// Return the ideal number of decimals based on the spinBox value or
+  /// 16 if there is no "good" number of decimals.
+  /// \sa ctk::significantDecimals()
+  static int spinBoxSignificantDecimals(ctkDoubleSpinBox* spinBox);
+
   int     Decimals;
   ctkDoubleSpinBox::DecimalsOptions DecimalsOption;
   double  SingleStep;

+ 118 - 95
Libs/Widgets/ctkDoubleSpinBox.cpp

@@ -174,6 +174,10 @@ ctkDoubleSpinBoxPrivate::ctkDoubleSpinBoxPrivate(ctkDoubleSpinBox& object)
   this->DOption = ctkDoubleSpinBox::DecimalsByShortcuts
     | ctkDoubleSpinBox::InsertDecimals;
   this->InvertedControls = false;
+  this->InputValue = 0.;
+  this->InputRange[0] = 0.;
+  this->InputRange[1] = 99.99;
+  this->ForceInputValueUpdate = false;
 }
 
 //-----------------------------------------------------------------------------
@@ -188,6 +192,9 @@ void ctkDoubleSpinBoxPrivate::init()
                    this, SLOT(editorTextChanged(QString)));
   this->SpinBox->setLineEdit(lineEdit);
   lineEdit->setObjectName(QLatin1String("qt_spinbox_lineedit"));
+  this->InputValue = this->SpinBox->value();
+  this->InputRange[0] = this->SpinBox->minimum();
+  this->InputRange[1] = this->SpinBox->maximum();
 
   QObject::connect(this->SpinBox, SIGNAL(valueChanged(double)),
     this, SLOT(onValueChanged()));
@@ -311,7 +318,9 @@ void ctkDoubleSpinBoxPrivate::editorTextChanged(const QString& text)
         decimals != this->SpinBox->decimals();
       if (changeDecimals)
         {
+        this->ForceInputValueUpdate = true;
         this->setValue(newValue, decimals);
+        this->ForceInputValueUpdate = false;
         }
       // else, let QDoubleSpinBox process the validation.
       }
@@ -471,33 +480,74 @@ double ctkDoubleSpinBoxPrivate
 void ctkDoubleSpinBoxPrivate::onValueChanged()
 {
   Q_Q(ctkDoubleSpinBox);
-  double value = q->value();
-  emit q->valueChanged(value);
-  emit q->valueChanged(QString::number(value, 'f', this->SpinBox->decimals()));
+  double newValue = this->SpinBox->value();
+  double oldValue = q->value();
+  if (this->Proxy)
+    {
+    oldValue = this->Proxy.data()->proxyValueFromValue(oldValue);
+    }
+  // Don't trigger value changed signal if the difference only happened on the
+  // precision.
+  if (this->compare(oldValue, newValue) && !this->ForceInputValueUpdate)
+    {
+    return;
+    }
+  // Force it only once (when the user typed a new number that could have change
+  // the number of decimals which could have make the compare test always pass.
+  this->ForceInputValueUpdate = false;
+
+  double minimum = q->minimum();
+  double maximum = q->maximum();
+  if (this->Proxy)
+    {
+    minimum = this->Proxy.data()->proxyValueFromValue(minimum);
+    maximum = this->Proxy.data()->proxyValueFromValue(maximum);
+    }
+  // Special case to return max precision
+  if (this->compare(minimum, newValue))
+    {
+    newValue = q->minimum();
+    }
+  else if (this->compare(maximum, newValue))
+    {
+    newValue = q->maximum();
+    }
+  else if (this->Proxy)
+    {
+    newValue = this->Proxy.data()->valueFromProxyValue(newValue);
+    }
+  this->InputValue = newValue;
+  emit q->valueChanged(newValue);
+  // \tbd The string might not make much sense when using proxies.
+  emit q->valueChanged(
+    QString::number(newValue, 'f', this->SpinBox->decimals()));
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBoxPrivate::onValueProxyAboutToBeModified()
 {
-  Q_Q(ctkDoubleSpinBox);
-  this->SpinBox->setProperty("inputValue", q->value());
-  this->SpinBox->setProperty("inputMinimum", q->minimum());
-  this->SpinBox->setProperty("inputMaximum", q->maximum());
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBoxPrivate::onValueProxyModified()
 {
   Q_Q(ctkDoubleSpinBox);
-  int decimals = q->decimals();
+  int oldDecimals = q->decimals();
+  double oldValue = this->InputValue;
+  ctkDoubleSpinBox::SetMode oldSetMode = this->Mode;
+
+  // Only the display is changed, not the programatic value, no need to trigger
+  // signals
   bool wasBlocking = q->blockSignals(true);
-  q->setRange(this->SpinBox->property("inputMinimum").toDouble(),
-              this->SpinBox->property("inputMaximum").toDouble());
-  q->setValue(this->SpinBox->property("inputValue").toDouble());
-  q->value();
+  // Enforce a refresh. Signals are blocked so it should not trigger unwanted
+  // signals
+  this->Mode = ctkDoubleSpinBox::SetAlways;
+  q->setRange(this->InputRange[0], this->InputRange[1]);
+  q->setValue(oldValue);
+  this->Mode = oldSetMode;
   q->blockSignals(wasBlocking);
-  // only decimals can change when value proxy is modified.
-  if (decimals != q->decimals())
+  // Decimals might change when value proxy is modified.
+  if (oldDecimals != q->decimals())
     {
     emit q->decimalsChanged(q->decimals());
     }
@@ -528,12 +578,7 @@ ctkDoubleSpinBox::ctkDoubleSpinBox(ctkDoubleSpinBox::SetMode mode, QWidget* newP
 double ctkDoubleSpinBox::value() const
 {
   Q_D(const ctkDoubleSpinBox);
-  double val = d->SpinBox->value();
-  if (d->Proxy)
-    {
-    val = d->Proxy.data()->valueFromProxyValue(val);
-    }
-  return val;
+  return d->InputValue;
 }
 
 //-----------------------------------------------------------------------------
@@ -677,83 +722,53 @@ void ctkDoubleSpinBox::setSingleStep(double newStep)
 double ctkDoubleSpinBox::minimum() const
 {
   Q_D(const ctkDoubleSpinBox);
-  double min = d->SpinBox->minimum();
-  double max = d->SpinBox->maximum();
-  if (d->Proxy)
-    {
-    min = d->Proxy.data()->valueFromProxyValue(min);
-    max = d->Proxy.data()->valueFromProxyValue(max);
-    }
-  return qMin(min, max);
+  return d->InputRange[0];
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBox::setMinimum(double newMin)
 {
-  Q_D(ctkDoubleSpinBox);
-  if (d->Proxy)
-    {
-    newMin = d->Proxy.data()->proxyValueFromValue(newMin);
-    }
-  if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
-      && d->compare(newMin, d->SpinBox->minimum()))
-    {
-    return;
-    }
-
-  d->SpinBox->setMinimum(newMin);
+  this->setRange(newMin, qMax(newMin, this->maximum()));
 }
 
 //-----------------------------------------------------------------------------
 double ctkDoubleSpinBox::maximum() const
 {
   Q_D(const ctkDoubleSpinBox);
-  double min = d->SpinBox->minimum();
-  double max = d->SpinBox->maximum();
-  if (d->Proxy)
-    {
-    min = d->Proxy.data()->valueFromProxyValue(min);
-    max = d->Proxy.data()->valueFromProxyValue(max);
-    }
-  return qMax(min, max);
+  return d->InputRange[1];
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBox::setMaximum(double newMax)
 {
-  Q_D(ctkDoubleSpinBox);
-  if (d->Proxy)
-    {
-    newMax = d->Proxy.data()->proxyValueFromValue(newMax);
-    }
-  if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
-    && d->compare(newMax, d->SpinBox->maximum()))
-    {
-    return;
-    }
-
-  d->SpinBox->setMaximum(newMax);
+  this->setRange(qMin(newMax, this->minimum()), newMax);
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBox::setRange(double newMin, double newMax)
 {
   Q_D(ctkDoubleSpinBox);
-  if (d->Proxy)
-    {
-    newMin = d->Proxy.data()->proxyValueFromValue(newMin);
-    newMax = d->Proxy.data()->proxyValueFromValue(newMax);
-    }
   if (newMin > newMax)
     {
     qSwap(newMin, newMax);
     }
   if (d->Mode == ctkDoubleSpinBox::SetIfDifferent
-      && d->compare(newMax, d->SpinBox->maximum())
-      && d->compare(newMin, d->SpinBox->minimum()))
+      && newMin == d->InputRange[0]
+      && newMax == d->InputRange[1])
     {
     return;
     }
+  d->InputRange[0] = newMin;
+  d->InputRange[1] = newMax;
+  if (d->Proxy)
+    {
+    newMin = d->Proxy.data()->proxyValueFromValue(newMin);
+    newMax = d->Proxy.data()->proxyValueFromValue(newMax);
+    if (newMin > newMax)
+      {
+      qSwap(newMin, newMax);
+      }
+    }
 
   d->SpinBox->setRange(newMin, newMax);
 }
@@ -779,13 +794,13 @@ void ctkDoubleSpinBox::setDecimals(int dec)
   d->DefaultDecimals = dec;
   // The number of decimals may or may not depend on the value. Recompute the
   // new number of decimals.
-  double value = this->value();
+  double currentValue = this->value();
   if (d->Proxy)
     {
-    value = d->Proxy.data()->proxyValueFromValue(value);
+    currentValue = d->Proxy.data()->proxyValueFromValue(currentValue);
     }
-  int newDecimals = d->decimalsForValue(value);
-  d->setDecimals(newDecimals);
+  int newDecimals = d->decimalsForValue(currentValue);
+  d->setValue(currentValue, newDecimals);
 }
 
 //-----------------------------------------------------------------------------
@@ -827,33 +842,33 @@ void ctkDoubleSpinBox::setValue(double value)
 void ctkDoubleSpinBox::setValueIfDifferent(double newValue)
 {
   Q_D(ctkDoubleSpinBox);
-  double newValueToDisplay = newValue;
-  if (d->Proxy)
+  if (newValue == d->InputValue)
     {
-    newValueToDisplay = d->Proxy.data()->proxyValueFromValue(newValueToDisplay);
-    }
-
-  int newValueDecimals = d->decimalsForValue(newValueToDisplay);
-  if (d->SpinBox->value() != d->round(newValueToDisplay, newValueDecimals)
-      || d->SpinBox->decimals() != newValueDecimals)
-    {
-    // Pass newValue and not newValueToDisplay as setValueAlways also computes the
-    // proxyValue from the given value
-    this->setValueAlways(newValue);
+    return;
     }
+  this->setValueAlways(newValue);
 }
 
 //-----------------------------------------------------------------------------
-void ctkDoubleSpinBox::setValueAlways(double value)
+void ctkDoubleSpinBox::setValueAlways(double newValue)
 {
   Q_D(ctkDoubleSpinBox);
+  newValue = qBound(d->InputRange[0], newValue, d->InputRange[1]);
+  const bool valueModified = d->InputValue != newValue;
+  d->InputValue = newValue;
+  double newValueToDisplay = newValue;
   if (d->Proxy)
     {
-    value = d->Proxy.data()->proxyValueFromValue(value);
+    newValueToDisplay = d->Proxy.data()->proxyValueFromValue(newValueToDisplay);
+    }
+  const int decimals = d->decimalsForValue(newValueToDisplay);
+  d->setValue(newValueToDisplay, decimals);
+  const bool signalsEmitted = (newValue != d->InputValue);
+  if (valueModified && !signalsEmitted)
+    {
+    emit valueChanged(d->InputValue);
+    emit valueChanged(QString::number(d->InputValue, 'f', d->SpinBox->decimals()));
     }
-
-  int decimals = d->decimalsForValue(value);
-  d->setValue(value, decimals);
 }
 
 //-----------------------------------------------------------------------------
@@ -975,26 +990,34 @@ bool ctkDoubleSpinBox::eventFilter(QObject* obj, QEvent* event)
     {
     QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
     Q_ASSERT(keyEvent);
+    int newDecimals = -1;
     if (keyEvent->modifiers() & Qt::ControlModifier)
       {
       if (keyEvent->key() == Qt::Key_Plus
         || keyEvent->key() == Qt::Key_Equal)
         {
-        d->setDecimals(this->decimals() + 1);
-        return true;
+        newDecimals = this->decimals() + 1;
         }
       else if (keyEvent->key() == Qt::Key_Minus)
         {
-        d->setDecimals(this->decimals() - 1);
-        return true;
+        newDecimals = this->decimals() - 1;
         }
       else if (keyEvent->key() == Qt::Key_0)
         {
-        d->setDecimals(d->DefaultDecimals);
-        return true;
+        newDecimals = d->DefaultDecimals;
         }
       }
-
+    if (newDecimals != -1)
+      {
+      double currentValue = this->value();
+      if (d->Proxy)
+        {
+        currentValue = d->Proxy.data()->proxyValueFromValue(currentValue);
+        }
+      // increasing the number of decimals should restore lost precision
+      d->setValue(currentValue, newDecimals);
+      return true;
+      }
     return QWidget::eventFilter(obj, event);
     }
   else

+ 1 - 0
Libs/Widgets/ctkDoubleSpinBox.h

@@ -301,6 +301,7 @@ protected:
   /// Reimplemented to support shortcuts on the double spinbox.
   virtual bool eventFilter(QObject *obj, QEvent *event);
 
+  friend class ctkCoordinatesWidget;
 private:
   Q_DECLARE_PRIVATE(ctkDoubleSpinBox);
   Q_DISABLE_COPY(ctkDoubleSpinBox);

+ 4 - 0
Libs/Widgets/ctkDoubleSpinBox_p.h

@@ -87,10 +87,14 @@ public:
   ctkDoubleSpinBox::DecimalsOptions DOption;
   bool InvertedControls;
 
+  double InputValue;
+  double InputRange[2];
+
   mutable QString CachedText;
   mutable double CachedValue;
   mutable QValidator::State CachedState;
   mutable int CachedDecimals;
+  bool ForceInputValueUpdate;
 
   QWeakPointer<ctkValueProxy> Proxy;
 

+ 2 - 1
Libs/Widgets/ctkRangeWidget.cpp

@@ -426,7 +426,8 @@ void ctkRangeWidget::setValues(double newMinimumValue, double newMaximumValue)
     {
     qSwap(newMinimumValue, newMaximumValue);
     }
-  const bool minimumFirst = (newMinimumValue <= this->maximumValue());
+  // This test must take into account NaN values
+  const bool minimumFirst = !(newMinimumValue > this->maximumValue());
 
   // disable the tracking temporally to emit the
   // signal valueChanged if changeValue() is called