File indexing completed on 2025-02-16 07:33:36
0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com> 0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT 0003 0004 // First included header is the public header of the class we are testing; 0005 // this forces the header to be self-contained. 0006 #include "multispinbox.h" 0007 // Second, the private implementation. 0008 #include "multispinbox_p.h" // IWYU pragma: keep 0009 0010 #include "constpropagatinguniquepointer.h" 0011 #include "multispinboxsection.h" 0012 #include <qabstractspinbox.h> 0013 #include <qaction.h> 0014 #include <qapplication.h> 0015 #include <qdebug.h> 0016 #include <qglobal.h> 0017 #include <qlabel.h> 0018 #include <qlineedit.h> 0019 #include <qlist.h> 0020 #include <qlocale.h> 0021 #include <qnamespace.h> 0022 #include <qobject.h> 0023 #include <qscopedpointer.h> 0024 #include <qsignalspy.h> 0025 #include <qsize.h> 0026 #include <qspinbox.h> 0027 #include <qstring.h> 0028 #include <qstringliteral.h> 0029 #include <qtest.h> 0030 #include <qtestcase.h> 0031 #include <qtestdata.h> 0032 #include <qtestkeyboard.h> 0033 #include <qvariant.h> 0034 #include <qwidget.h> 0035 0036 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0037 #include <qtmetamacros.h> 0038 #else 0039 #include <qobjectdefs.h> 0040 #endif 0041 0042 // From Qt documentation: 0043 // “Note: This function is not declared in any of Qt's header files. To 0044 // use it in your application, declare the function prototype before 0045 // calling it.” 0046 void qt_set_sequence_auto_mnemonic(bool b); 0047 0048 static void snippet02() 0049 { 0050 //! [MultiSpinBox Basic example] 0051 PerceptualColor::MultiSpinBox *myHsvSpinBox = // 0052 new PerceptualColor::MultiSpinBox(); 0053 PerceptualColor::MultiSpinBoxSection myConfiguration; 0054 QList<PerceptualColor::MultiSpinBoxSection> hsvConfigurations; 0055 0056 myConfiguration.setDecimals(1); 0057 0058 myConfiguration.setPrefix(QString()); 0059 myConfiguration.setMinimum(0); 0060 myConfiguration.setWrapping(true); 0061 myConfiguration.setMaximum(360); 0062 myConfiguration.setSuffix(QStringLiteral(u"° ")); 0063 hsvConfigurations.append(myConfiguration); 0064 0065 myConfiguration.setPrefix(QStringLiteral(u" ")); 0066 myConfiguration.setMinimum(0); 0067 myConfiguration.setMaximum(255); 0068 myConfiguration.setWrapping(false); 0069 myConfiguration.setSuffix(QStringLiteral(u" ")); 0070 hsvConfigurations.append(myConfiguration); 0071 0072 myConfiguration.setSuffix(QString()); 0073 hsvConfigurations.append(myConfiguration); 0074 0075 myHsvSpinBox->setSectionConfigurations(hsvConfigurations); 0076 0077 myHsvSpinBox->setSectionValues(QList<double>{310, 200, 100}); 0078 // Initial content is: 310,0° 200,0 100,0 0079 //! [MultiSpinBox Basic example] 0080 delete myHsvSpinBox; 0081 } 0082 0083 class testSnippet02 : public PerceptualColor::MultiSpinBox 0084 { 0085 Q_OBJECT 0086 0087 //! [MultiSpinBox Full-featured interface] 0088 Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) 0089 Q_PROPERTY(int sectionCount READ sectionCount NOTIFY sectionCountChanged) 0090 0091 public: 0092 void addSection(PerceptualColor::MultiSpinBoxSection newSection); 0093 void addSections(QList<PerceptualColor::MultiSpinBoxSection> newSections); 0094 void append(PerceptualColor::MultiSpinBoxSection newSection); 0095 void append(QList<PerceptualColor::MultiSpinBoxSection> newSections); 0096 QString cleanText(int index) const; // See also “cleanText” 0097 void clearSections(); 0098 int currentIndex() const; 0099 PerceptualColor::MultiSpinBoxSection currentSection() const; 0100 PerceptualColor::MultiSpinBoxSection firstSection() const; 0101 void insertSection(int index, PerceptualColor::MultiSpinBoxSection newSection); 0102 void insertSection(int index, QList<PerceptualColor::MultiSpinBoxSection> newSections); 0103 PerceptualColor::MultiSpinBoxSection lastSection() const; 0104 void moveSection(int from, int to); 0105 void prependSection(PerceptualColor::MultiSpinBoxSection newSection); 0106 void prependSections(QList<PerceptualColor::MultiSpinBoxSection> newSections); 0107 void removeFirstSection(); 0108 void removeLastSection(); 0109 void removeSection(int index); 0110 void replaceSection(int index, PerceptualColor::MultiSpinBoxSection newSection); 0111 PerceptualColor::MultiSpinBoxSection sectionAt(int index) const; 0112 int sectionCount() const; // Somewhat redundant with MultiSpinBox::sections().count() 0113 QList<PerceptualColor::MultiSpinBoxSection> sectionConfigurations() const; 0114 QString sectionText(int index) const; // See also “cleanText” 0115 void setSelectedSection(int index); // A better name might be “selectSection” 0116 void setSectionConfigurations(const QList<PerceptualColor::MultiSpinBoxSection> &newSections); 0117 void swapSections(int i, int j); 0118 0119 // What about these functions? They… 0120 // …are public in QDoubleSpinBox 0121 // …are protected in QSpinBox 0122 // …do not exist in QDateTimeEdit 0123 // …do not exist in QAbstractSpinBox: 0124 QString textFromValue(double value) const; 0125 double valueFromText(const QString &text) const; 0126 0127 Q_SIGNALS: 0128 void currentIndexChanged(int newCurrentIndex); 0129 void sectionCountChanged(int newSectionCount); 0130 0131 // The following signal in Qt 5.15 seems to not always be emitted 0132 // when actually text changes, but instead only if actually the value 0133 // changes. So if the text changes from “0.1” to “0.10” this signal 0134 // is not emitted because the value itself did not change. This is 0135 // counter-intuitive because the behaviour does not correspond to the 0136 // name of the signal. It’s therefore better not no implement this 0137 // signal. 0138 void textChanged(const QString &newText); 0139 0140 // The following signal is emitted always when the value changes. 0141 // If we separate the section configuration from the section value, 0142 // we could provide the new value of type QList<double> as an 0143 // argument of this signal and also declare a property corresponding 0144 // to this signal. 0145 void valueChanged(); 0146 0147 public Q_SLOTS: 0148 void setCurrentIndex(int newIndex); 0149 //! [MultiSpinBox Full-featured interface] 0150 }; 0151 int testSnippet02::currentIndex() const 0152 { 0153 return 0; 0154 } 0155 void testSnippet02::setCurrentIndex(int newIndex) 0156 { 0157 Q_UNUSED(newIndex) 0158 } 0159 int testSnippet02::sectionCount() const 0160 { 0161 return 0; 0162 } 0163 0164 namespace PerceptualColor 0165 { 0166 class TestMultiSpinBox : public QObject 0167 { 0168 Q_OBJECT 0169 0170 public: 0171 explicit TestMultiSpinBox(QObject *parent = nullptr) 0172 : QObject(parent) 0173 { 0174 } 0175 0176 private: 0177 QList<MultiSpinBoxSection> exampleConfigurations; 0178 static void voidMessageHandler(QtMsgType, const QMessageLogContext &, const QString &) 0179 { 0180 // dummy message handler that does not print messages 0181 } 0182 0183 private Q_SLOTS: 0184 void initTestCase() 0185 { 0186 // Called before the first test function is executed 0187 0188 // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit") 0189 // enabled, also on platforms that disable it by default. 0190 qt_set_sequence_auto_mnemonic(true); 0191 0192 // Provide example configuration 0193 MultiSpinBoxSection mySection; 0194 mySection.setDecimals(0); 0195 mySection.setMinimum(0); 0196 mySection.setMaximum(360); 0197 mySection.setPrefix(QString()); 0198 mySection.setSuffix(QStringLiteral(u"°")); 0199 exampleConfigurations.append(mySection); 0200 mySection.setMaximum(100); 0201 mySection.setPrefix(QStringLiteral(u" ")); 0202 mySection.setSuffix(QStringLiteral(u"%")); 0203 exampleConfigurations.append(mySection); 0204 mySection.setMaximum(255); 0205 mySection.setPrefix(QStringLiteral(u" ")); 0206 mySection.setSuffix(QString()); 0207 exampleConfigurations.append(mySection); 0208 } 0209 0210 void cleanupTestCase() 0211 { 0212 // Called after the last test function was executed 0213 } 0214 0215 void init() 0216 { 0217 // Called before each test function is executed 0218 } 0219 0220 void cleanup() 0221 { 0222 // Called after every test function 0223 } 0224 0225 void testDefaultValues() 0226 { 0227 // The default values should be the same as for QDoubleSpinBox 0228 MultiSpinBox myMulti; 0229 QDoubleSpinBox myDoubleSpinBox; 0230 0231 // Test default section values 0232 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 0233 0234 // Test default values of the configuration directly in the widget 0235 QCOMPARE(myMulti.sectionConfigurations().at(0).decimals(), // 0236 myDoubleSpinBox.decimals()); 0237 QCOMPARE(myMulti.sectionConfigurations().at(0).isWrapping(), // 0238 myDoubleSpinBox.wrapping()); 0239 QCOMPARE(myMulti.sectionConfigurations().at(0).maximum(), // 0240 myDoubleSpinBox.maximum()); 0241 QCOMPARE(myMulti.sectionConfigurations().at(0).minimum(), // 0242 myDoubleSpinBox.minimum()); 0243 QCOMPARE(myMulti.sectionConfigurations().at(0).prefix(), // 0244 myDoubleSpinBox.prefix()); 0245 QCOMPARE(myMulti.sectionConfigurations().at(0).singleStep(), // 0246 myDoubleSpinBox.singleStep()); 0247 QCOMPARE(myMulti.sectionConfigurations().at(0).suffix(), // 0248 myDoubleSpinBox.suffix()); 0249 0250 // Whitebox tests 0251 QCOMPARE(myMulti.sectionValues(), QList<double>{0}); 0252 QCOMPARE(myMulti.d_pointer->m_sectionValues, QList<double>{0}); 0253 QCOMPARE(myMulti.d_pointer->m_currentIndex, 0); 0254 } 0255 0256 void testConstructor() 0257 { 0258 // Test the the constructor does not crash 0259 PerceptualColor::MultiSpinBox myMulti; 0260 // Test basic constructor results 0261 QVERIFY2(myMulti.d_pointer->m_sectionConfigurations.length() > 0, // 0262 "Make sure the default configuration has at least 1 section."); 0263 } 0264 0265 void testInteraction() 0266 { 0267 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 0268 new PerceptualColor::MultiSpinBox()); 0269 widget->setSectionConfigurations(exampleConfigurations); 0270 // Assert that the setup is okay. 0271 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"0° 0% 0")); 0272 // Go to begin of the line edit 0273 for (int i = 0; i < 10; i++) { 0274 // NOTE Simply use 1 time Qt::Key_Home would be easier 0275 // than calling 10 times Qt::Key_Left, however Qt::Key_Home 0276 // does not work on MacOS. 0277 QTest::keyClick(widget.data(), Qt::Key_Left); 0278 } 0279 QCOMPARE(widget->lineEdit()->selectedText(), QString()); 0280 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"0° 0% 0")); 0281 QCOMPARE(widget->lineEdit()->cursorPosition(), 0); 0282 // Select the first “0”: 0283 QTest::keyClick(widget.data(), Qt::Key_Right, Qt::ShiftModifier, 0); 0284 // The content shouldn’t have changed. 0285 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"0° 0% 0")); 0286 // The selection should contain “0”. 0287 QCOMPARE(widget->lineEdit()->selectedText(), QStringLiteral(u"0")); 0288 // Write “45” 0289 QTest::keyClicks(widget.data(), QStringLiteral(u"45")); 0290 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0291 // Select “45” 0292 QTest::keyClick(widget.data(), Qt::Key_Left, Qt::ShiftModifier, 0); 0293 QTest::keyClick(widget.data(), Qt::Key_Left, Qt::ShiftModifier, 0); 0294 // Copy to clipboard 0295 // TODO The following line that copies to clipboard 0296 // is surprisingly extremly slow. 0297 QTest::keyClick(widget.data(), Qt::Key_C, Qt::ControlModifier, 0); 0298 // Go to the left 0299 QTest::keyClick(widget.data(), Qt::Key_Left); 0300 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0301 // Go to second section 0302 QTest::keyClick(widget.data(), Qt::Key_Right); 0303 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0304 QTest::keyClick(widget.data(), Qt::Key_Right); 0305 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0306 QTest::keyClick(widget.data(), Qt::Key_Right); 0307 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0308 QTest::keyClick(widget.data(), Qt::Key_Right); 0309 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0310 QTest::keyClick(widget.data(), Qt::Key_Right); 0311 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 0% 0")); 0312 // Select second section: 0313 QTest::keyClick(widget.data(), Qt::Key_Right, Qt::ShiftModifier, 0); 0314 QCOMPARE(widget->lineEdit()->selectedText(), QStringLiteral(u"0")); 0315 // Take “45” from clipboard 0316 QTest::keyClick(widget.data(), Qt::Key_V, Qt::ControlModifier, 0); 0317 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 45% 0")); 0318 QTest::keyClick(widget.data(), Qt::Key_Right); 0319 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 45% 0")); 0320 QTest::keyClick(widget.data(), Qt::Key_Right); 0321 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 45% 0")); 0322 QTest::keyClick(widget.data(), Qt::Key_Right); 0323 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 45% 0")); 0324 QTest::keyClick(widget.data(), Qt::Key_Right); 0325 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"45° 45% 0")); 0326 } 0327 0328 void testCurrentSectionIndex() 0329 { 0330 MultiSpinBox test; 0331 // Test default index 0332 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0333 0334 // suppress warnings 0335 qInstallMessageHandler(voidMessageHandler); 0336 // Test if setting negative value is ignored 0337 QVERIFY_EXCEPTION_THROWN( // 0338 test.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(-1), // 0339 int); 0340 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0341 QVERIFY_EXCEPTION_THROWN( // 0342 test.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(-100), // 0343 int); 0344 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0345 // Test setting too high values is ignored 0346 QVERIFY_EXCEPTION_THROWN( // 0347 test.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(100), // 0348 int); 0349 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0350 // do not suppress warning for generating invalid QColor anymore 0351 qInstallMessageHandler(nullptr); 0352 0353 // Test if correct sections are stored correctly 0354 QList<MultiSpinBoxSection> mySectionList; 0355 mySectionList.append(MultiSpinBoxSection()); 0356 mySectionList.append(MultiSpinBoxSection()); 0357 mySectionList.append(MultiSpinBoxSection()); 0358 test.setSectionConfigurations(mySectionList); 0359 test.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(2); 0360 QCOMPARE(test.d_pointer->m_currentIndex, 2); 0361 } 0362 0363 void testSetConfiguration() 0364 { 0365 // Correct configurations should be applied as-is. 0366 QList<MultiSpinBoxSection> myConfigurations; 0367 myConfigurations.append(MultiSpinBoxSection()); 0368 myConfigurations.append(MultiSpinBoxSection()); 0369 myConfigurations.append(MultiSpinBoxSection()); 0370 MultiSpinBox test; 0371 QCOMPARE(test.sectionConfigurations().count(), 1); 0372 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0373 test.setSectionConfigurations(myConfigurations); 0374 QCOMPARE(test.sectionConfigurations().count(), 3); 0375 QCOMPARE(test.d_pointer->m_currentIndex, 0); 0376 0377 // Empty configurations shall be ignored 0378 test.setSectionConfigurations(QList<MultiSpinBoxSection>()); 0379 QCOMPARE(test.sectionConfigurations().count(), 3); 0380 0381 // Invalid values should be adapted 0382 myConfigurations.clear(); 0383 MultiSpinBoxSection myInvalidSection; 0384 myInvalidSection.setMinimum(50); 0385 myInvalidSection.setMaximum(30); 0386 myConfigurations.append(myInvalidSection); 0387 test.setSectionConfigurations(myConfigurations); 0388 QList<double> myValues; 0389 myValues.clear(); 0390 myValues.append(40); 0391 test.setSectionValues(myValues); 0392 QVERIFY2( 0393 // condition 0394 test.d_pointer->m_sectionConfigurations.at(0).minimum() // 0395 <= test.d_pointer->m_sectionConfigurations.at(0).maximum(), 0396 // comment 0397 "minimum <= maximum"); 0398 QVERIFY2( 0399 // condition 0400 test.d_pointer->m_sectionConfigurations.at(0).minimum() // 0401 <= test.d_pointer->m_sectionValues.at(0), 0402 // comment 0403 "minimum <= value"); 0404 QVERIFY2( 0405 // condition 0406 test.d_pointer->m_sectionValues.at(0) // 0407 <= test.d_pointer->m_sectionConfigurations.at(0).maximum(), 0408 // comment 0409 "value <= maximum"); 0410 0411 // Invalid values should be adapted 0412 myConfigurations.clear(); 0413 myInvalidSection.setMinimum(-50); 0414 myInvalidSection.setMaximum(-70); 0415 myConfigurations.append(myInvalidSection); 0416 myValues.clear(); 0417 myValues.append(-60); 0418 test.setSectionConfigurations(myConfigurations); 0419 QVERIFY2( 0420 // condition 0421 test.d_pointer->m_sectionConfigurations.at(0).minimum() // 0422 <= test.d_pointer->m_sectionConfigurations.at(0).maximum(), 0423 // comment 0424 "minimum <= maximum"); 0425 QVERIFY2( 0426 // condition 0427 test.d_pointer->m_sectionConfigurations.at(0).minimum() // 0428 <= test.d_pointer->m_sectionValues.at(0), 0429 // comment 0430 "minimum <= value"); 0431 QVERIFY2( 0432 // condition 0433 test.d_pointer->m_sectionValues.at(0) // 0434 <= test.d_pointer->m_sectionConfigurations.at(0).maximum(), 0435 // comment 0436 "value <= maximum"); 0437 } 0438 0439 void testMinimalSizeHint() 0440 { 0441 PerceptualColor::MultiSpinBox myMulti; 0442 QCOMPARE(myMulti.minimumSizeHint(), myMulti.sizeHint()); 0443 myMulti.setSectionConfigurations(exampleConfigurations); 0444 QCOMPARE(myMulti.minimumSizeHint(), myMulti.sizeHint()); 0445 } 0446 0447 void testSizeHint() 0448 { 0449 PerceptualColor::MultiSpinBox myMulti; 0450 // Example configuration with long prefix and suffix to make 0451 // sure being bigger than the default minimal widget size. 0452 QList<MultiSpinBoxSection> config; 0453 MultiSpinBoxSection section; 0454 section.setMinimum(1); 0455 section.setMaximum(9); 0456 section.setPrefix(QStringLiteral(u"abcdefghij")); 0457 section.setSuffix(QStringLiteral(u"abcdefghij")); 0458 config.append(section); 0459 myMulti.setSectionConfigurations(config); 0460 const int referenceWidth = myMulti.sizeHint().width(); 0461 0462 // Now test various configurations that should lead to bigger sizes… 0463 0464 section.setMinimum(-1); 0465 section.setMaximum(9); 0466 section.setPrefix(QStringLiteral(u"abcdefghij")); 0467 section.setSuffix(QStringLiteral(u"abcdefghij")); 0468 config.clear(); 0469 config.append(section); 0470 myMulti.setSectionConfigurations(config); 0471 QVERIFY(myMulti.sizeHint().width() > referenceWidth); 0472 0473 section.setMinimum(1); 0474 section.setMaximum(19); 0475 section.setPrefix(QStringLiteral(u"abcdefghij")); 0476 section.setSuffix(QStringLiteral(u"abcdefghij")); 0477 config.clear(); 0478 config.append(section); 0479 myMulti.setSectionConfigurations(config); 0480 QVERIFY(myMulti.sizeHint().width() > referenceWidth); 0481 0482 section.setMinimum(-1); 0483 section.setMaximum(9); 0484 section.setPrefix(QStringLiteral(u"abcdefghijh")); 0485 section.setSuffix(QStringLiteral(u"abcdefghij")); 0486 config.clear(); 0487 config.append(section); 0488 myMulti.setSectionConfigurations(config); 0489 QVERIFY(myMulti.sizeHint().width() > referenceWidth); 0490 0491 section.setMinimum(-1); 0492 section.setMaximum(9); 0493 section.setPrefix(QStringLiteral(u"abcdefghij")); 0494 section.setSuffix(QStringLiteral(u"abcdefghijh")); 0495 config.clear(); 0496 config.append(section); 0497 myMulti.setSectionConfigurations(config); 0498 QVERIFY(myMulti.sizeHint().width() > referenceWidth); 0499 } 0500 0501 void testUpdatePrefixValueSuffixText() 0502 { 0503 PerceptualColor::MultiSpinBox myMulti; 0504 // Example configuration with long prefix and suffix to make 0505 // sure being bigger than the default minimal widget size. 0506 QList<MultiSpinBoxSection> myConfigurations; 0507 MultiSpinBoxSection myConfiguration; 0508 QList<double> myValues; 0509 0510 myConfiguration.setDecimals(0); 0511 myConfiguration.setMinimum(1); 0512 myConfiguration.setMaximum(9); 0513 myConfiguration.setPrefix(QStringLiteral(u"abc")); 0514 myConfiguration.setSuffix(QStringLiteral(u"def")); 0515 myConfigurations.append(myConfiguration); 0516 myValues.append(8); 0517 0518 myConfiguration.setMinimum(10); 0519 myConfiguration.setMaximum(90); 0520 myConfiguration.setPrefix(QStringLiteral(u"ghi")); 0521 myConfiguration.setSuffix(QStringLiteral(u"jkl")); 0522 myConfigurations.append(myConfiguration); 0523 myValues.append(80); 0524 0525 myMulti.setSectionConfigurations(myConfigurations); 0526 myMulti.setSectionValues(myValues); 0527 myMulti.d_pointer->m_currentIndex = 1; 0528 myMulti.d_pointer->updatePrefixValueSuffixText(); 0529 QCOMPARE(myMulti.d_pointer->m_textBeforeCurrentValue, // 0530 QStringLiteral(u"abc8defghi")); 0531 QCOMPARE(myMulti.d_pointer->m_textOfCurrentValue, // 0532 QStringLiteral(u"80")); 0533 QCOMPARE(myMulti.d_pointer->m_textAfterCurrentValue, // 0534 QStringLiteral(u"jkl")); 0535 } 0536 0537 void testSetCurrentSectionIndexWithoutSelectingText() 0538 { 0539 PerceptualColor::MultiSpinBox myMulti; 0540 QList<MultiSpinBoxSection> myConfigurations; 0541 MultiSpinBoxSection myConfiguration; 0542 QList<double> myValues; 0543 0544 myConfiguration.setMinimum(1); 0545 myConfiguration.setMaximum(9); 0546 myConfiguration.setPrefix(QStringLiteral(u"abc")); 0547 myConfiguration.setSuffix(QStringLiteral(u"def")); 0548 myConfigurations.append(myConfiguration); 0549 myValues.append(8); 0550 0551 myConfiguration.setMinimum(10); 0552 myConfiguration.setMaximum(90); 0553 myConfiguration.setPrefix(QStringLiteral(u"ghi")); 0554 myConfiguration.setSuffix(QStringLiteral(u"jkl")); 0555 myConfigurations.append(myConfiguration); 0556 myValues.append(80); 0557 0558 myMulti.setSectionConfigurations(myConfigurations); 0559 myMulti.setSectionValues(myValues); 0560 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(1); 0561 QCOMPARE(myMulti.d_pointer->m_currentIndex, 1); 0562 QVERIFY2(!myMulti.lineEdit()->hasSelectedText(), // 0563 "No text should be selected."); 0564 } 0565 0566 void testSetCurrentSectionIndex() 0567 { 0568 PerceptualColor::MultiSpinBox myMulti; 0569 myMulti.setSectionConfigurations(exampleConfigurations); 0570 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(2); 0571 QCOMPARE(myMulti.d_pointer->m_currentIndex, 2); 0572 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(0); 0573 QCOMPARE(myMulti.d_pointer->m_currentIndex, 0); 0574 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(1); 0575 QCOMPARE(myMulti.d_pointer->m_currentIndex, 1); 0576 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(2); 0577 QCOMPARE(myMulti.d_pointer->m_currentIndex, 2); 0578 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(0); 0579 QCOMPARE(myMulti.d_pointer->m_currentIndex, 0); 0580 0581 QList<MultiSpinBoxSection> myConfigurations; 0582 MultiSpinBoxSection myConfiguration; 0583 QList<double> myValues; 0584 0585 myConfiguration.setMinimum(1); 0586 myConfiguration.setMaximum(9); 0587 myConfiguration.setPrefix(QStringLiteral(u"abc")); 0588 myConfiguration.setSuffix(QStringLiteral(u"def")); 0589 myConfigurations.append(myConfiguration); 0590 myValues.append(8); 0591 0592 myConfiguration.setMinimum(10); 0593 myConfiguration.setMaximum(90); 0594 myConfiguration.setPrefix(QStringLiteral(u"ghi")); 0595 myConfiguration.setSuffix(QStringLiteral(u"jkl")); 0596 myConfigurations.append(myConfiguration); 0597 myValues.append(80); 0598 0599 myMulti.setSectionConfigurations(myConfigurations); 0600 myMulti.setSectionValues(myValues); 0601 0602 myMulti.d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(1); 0603 QCOMPARE(myMulti.d_pointer->m_currentIndex, 1); 0604 QVERIFY2(!myMulti.lineEdit()->hasSelectedText(), 0605 "No text should be selected because invisible widgets " 0606 "have no focus."); 0607 } 0608 0609 void testStepEnabledSimple() 0610 { 0611 PerceptualColor::MultiSpinBox myMulti; 0612 QList<MultiSpinBoxSection> myConfigurations; 0613 MultiSpinBoxSection myConfiguration; 0614 QList<double> myValues; 0615 0616 myConfigurations.clear(); 0617 myConfiguration.setMinimum(1); 0618 myConfiguration.setMaximum(9); 0619 myConfiguration.setPrefix(QStringLiteral(u"abc")); 0620 myConfiguration.setSuffix(QStringLiteral(u"def")); 0621 myConfigurations.append(myConfiguration); 0622 myMulti.setSectionConfigurations(myConfigurations); 0623 myValues.clear(); 0624 myValues.append(8); 0625 myMulti.setSectionValues(myValues); 0626 QAbstractSpinBox::StepEnabled flags = myMulti.stepEnabled(); 0627 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0628 "Step up should be enabled"); 0629 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0630 "Step down should be enabled"); 0631 0632 myValues.clear(); 0633 myValues.append(9); 0634 myMulti.setSectionValues(myValues); 0635 flags = myMulti.stepEnabled(); 0636 QVERIFY2(!flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0637 "Step up should be disabled"); 0638 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0639 "Step down should be enabled"); 0640 0641 myValues.clear(); 0642 myValues.append(10); 0643 myMulti.setSectionValues(myValues); 0644 flags = myMulti.stepEnabled(); 0645 QVERIFY2(!flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0646 "Step up should be disabled"); 0647 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0648 "Step down should be enabled"); 0649 0650 myValues.clear(); 0651 myValues.append(1); 0652 myMulti.setSectionValues(myValues); 0653 flags = myMulti.stepEnabled(); 0654 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0655 "Step up should be enabled"); 0656 QVERIFY2(!flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0657 "Step down should be disabled"); 0658 0659 myValues.clear(); 0660 myValues.append(0); 0661 myMulti.setSectionValues(myValues); 0662 flags = myMulti.stepEnabled(); 0663 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0664 "Step up should be enabled"); 0665 QVERIFY2(!flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0666 "Step down should be disabled"); 0667 0668 myValues.clear(); 0669 myValues.append(-1); 0670 myMulti.setSectionValues(myValues); 0671 flags = myMulti.stepEnabled(); 0672 QVERIFY2(flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0673 "Step up should be enabled"); 0674 QVERIFY2(!flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0675 "Step down should be disabled"); 0676 } 0677 0678 void testStepEnabledAndSectionIndex_data() 0679 { 0680 QTest::addColumn<int>("cursorPosition"); 0681 QTest::addColumn<int>("sectionIndex"); 0682 QTest::addColumn<int>("minimum"); 0683 QTest::addColumn<int>("value"); 0684 QTest::addColumn<int>("maximum"); 0685 QTest::addColumn<bool>("StepUpEnabled"); 0686 QTest::addColumn<bool>("StepDownEnabled"); 0687 0688 QTest::newRow("0") << 0 << 0 << 0 << 0 << 360 << true << false; 0689 QTest::newRow("1") << 1 << 0 << 0 << 0 << 360 << true << false; 0690 QTest::newRow("2") << 2 << 0 << 0 << 0 << 360 << true << false; 0691 QTest::newRow("4") << 4 << 1 << 0 << 5 << 100 << true << true; 0692 QTest::newRow("5") << 5 << 1 << 0 << 5 << 100 << true << true; 0693 QTest::newRow("6") << 6 << 1 << 0 << 5 << 100 << true << true; 0694 QTest::newRow("8") << 8 << 2 << 0 << 0 << 255 << true << false; 0695 QTest::newRow("9") << 9 << 2 << 0 << 0 << 255 << true << false; 0696 } 0697 0698 void testStepEnabledAndSectionIndex() 0699 { 0700 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 0701 new PerceptualColor::MultiSpinBox()); 0702 QList<MultiSpinBoxSection> specialConfigurations = // 0703 exampleConfigurations; 0704 QList<double> myValues; 0705 while (myValues.count() < specialConfigurations.count()) { 0706 myValues.append(0); 0707 } 0708 const quint8 sampleSectionNumber = 1; 0709 const quint8 sampleValue = 5; 0710 widget->setSectionConfigurations(specialConfigurations); 0711 myValues[sampleSectionNumber] = sampleValue; 0712 widget->setSectionValues(myValues); 0713 widget->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue( // 0714 sampleSectionNumber); 0715 0716 // Assertions: Assert that the setup is okay. 0717 // Assert statements seem to be not always reliably within QTest. 0718 // Therefore we do some assertions here with QCOMPARE. 0719 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"0° 5% 0")); 0720 QCOMPARE(widget->sectionValues().at(sampleSectionNumber), sampleValue); 0721 QAbstractSpinBox::StepEnabled flags; 0722 0723 // Actual testing 0724 QFETCH(int, cursorPosition); 0725 QFETCH(int, sectionIndex); 0726 QFETCH(int, minimum); 0727 QFETCH(int, value); 0728 QFETCH(int, maximum); 0729 QFETCH(bool, StepUpEnabled); 0730 QFETCH(bool, StepDownEnabled); 0731 widget->lineEdit()->setCursorPosition(cursorPosition); 0732 if (widget->lineEdit()->text() != QStringLiteral(u"0° 5% 0")) { 0733 // Throw an exception instead of using an assert statement. 0734 // Assert statements seem to be not always reliably within QTest. 0735 throw 0; 0736 } 0737 flags = widget->stepEnabled(); 0738 const auto &d = widget->d_pointer; 0739 QCOMPARE(d->m_currentIndex, sectionIndex); 0740 QCOMPARE(d->m_sectionConfigurations.at(d->m_currentIndex).minimum(), // 0741 minimum); 0742 QCOMPARE(d->m_sectionValues.at(d->m_currentIndex), // 0743 value); 0744 QCOMPARE(d->m_sectionConfigurations.at(d->m_currentIndex).maximum(), // 0745 maximum); 0746 QCOMPARE(flags.testFlag(QAbstractSpinBox::StepUpEnabled), // 0747 StepUpEnabled); 0748 QCOMPARE(flags.testFlag(QAbstractSpinBox::StepDownEnabled), // 0749 StepDownEnabled); 0750 } 0751 0752 void testConfiguration() 0753 { 0754 PerceptualColor::MultiSpinBox myMulti; 0755 QList<MultiSpinBoxSection> config; 0756 MultiSpinBoxSection section; 0757 section.setMinimum(1); 0758 section.setMaximum(9); 0759 section.setPrefix(QStringLiteral(u"abc")); 0760 section.setSuffix(QStringLiteral(u"def")); 0761 config.append(section); 0762 myMulti.setSectionConfigurations(config); 0763 QCOMPARE(myMulti.sectionConfigurations().count(), 1); 0764 QCOMPARE(myMulti.sectionConfigurations().at(0).minimum(), 1); 0765 QCOMPARE(myMulti.sectionConfigurations().at(0).maximum(), 9); 0766 QCOMPARE(myMulti.sectionConfigurations().at(0).prefix(), // 0767 QStringLiteral(u"abc")); 0768 QCOMPARE(myMulti.sectionConfigurations().at(0).suffix(), // 0769 QStringLiteral(u"def")); 0770 } 0771 0772 void testFocusIntegrationForwardTab() 0773 { 0774 // Integration test for: 0775 // → MultiSpinBox::focusNextPrevChild() 0776 // → MultiSpinBox::focusInEvent() 0777 // → MultiSpinBox::focusOutEvent() 0778 QScopedPointer<QWidget> parentWidget(new QWidget()); 0779 QSpinBox *widget1 = new QSpinBox(parentWidget.data()); 0780 widget1->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0781 PerceptualColor::MultiSpinBox *widget2 = // 0782 new PerceptualColor::MultiSpinBox(parentWidget.data()); 0783 widget2->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0784 widget2->setSectionConfigurations(exampleConfigurations); 0785 QSpinBox *widget3 = new QSpinBox(parentWidget.data()); 0786 widget3->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0787 QLabel *label2 = new QLabel(QStringLiteral(u"&Test"), // 0788 parentWidget.data()); 0789 label2->setBuddy(widget2); 0790 widget1->setFocus(); 0791 parentWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0792 0793 // It is necessary to show the widget and make it active 0794 // to make focus and widget events working within unit tests. 0795 parentWidget->show(); 0796 QApplication::setActiveWindow(parentWidget.data()); 0797 0798 // Assert that the setup is okay. 0799 if (!widget1->hasFocus()) { 0800 // Throw an exception instead of using an assert statement. 0801 // Assert statements seem to be not always reliably within QTest. 0802 throw 0; 0803 } 0804 if (widget2->hasFocus()) { 0805 // Throw an exception instead of using an assert statement. 0806 // Assert statements seem to be not always reliably within QTest. 0807 throw 0; 0808 } 0809 if (widget3->hasFocus()) { 0810 // Throw an exception instead of using an assert statement. 0811 // Assert statements seem to be not always reliably within QTest. 0812 throw 0; 0813 } 0814 if (QApplication::focusWidget() != widget1) { 0815 // Throw an exception instead of using an assert statement. 0816 // Assert statements seem to be not always reliably within QTest. 0817 throw 0; 0818 } 0819 if (widget2->d_pointer->m_sectionConfigurations.count() != 3) { 0820 // Throw an exception instead of using an assert statement. 0821 // Assert statements seem to be not always reliably within QTest. 0822 throw 0; 0823 } 0824 0825 // Start actual testing 0826 0827 // Apparently it isn’t possible to call simply the key click 0828 // on the parent widget. This code fails sometimes: 0829 // QTest::keyClick(parentWidget, Qt::Key::Key_Tab); 0830 // Therefore, we call QTest::keyClick() on 0831 // QApplication::focusWidget() 0832 0833 // Move focus from widget1 to widget2/section0 0834 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 0835 QCOMPARE(QApplication::focusWidget(), widget2); 0836 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 0837 // Move focus from widget2/section0 to widget2/section1 0838 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 0839 QCOMPARE(QApplication::focusWidget(), widget2); 0840 QCOMPARE(widget2->d_pointer->m_currentIndex, 1); 0841 // Move focus from widget2/section1 to widget2/section2 0842 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 0843 QCOMPARE(QApplication::focusWidget(), widget2); 0844 QCOMPARE(widget2->d_pointer->m_currentIndex, 2); 0845 // Move focus from widget2/section2 to widget3 0846 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 0847 QCOMPARE(QApplication::focusWidget(), widget3); 0848 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 0849 } 0850 0851 void testFocusIntegrationBackwardTab() 0852 { 0853 // Integration test for: 0854 // → MultiSpinBox::focusNextPrevChild() 0855 // → MultiSpinBox::focusInEvent() 0856 // → MultiSpinBox::focusOutEvent() 0857 QScopedPointer<QWidget> parentWidget(new QWidget()); 0858 QSpinBox *widget1 = new QSpinBox(parentWidget.data()); 0859 widget1->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0860 PerceptualColor::MultiSpinBox *widget2 = // 0861 new PerceptualColor::MultiSpinBox(parentWidget.data()); 0862 widget2->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0863 widget2->setSectionConfigurations(exampleConfigurations); 0864 QSpinBox *widget3 = new QSpinBox(parentWidget.data()); 0865 widget3->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0866 QLabel *label2 = new QLabel(QStringLiteral(u"&Test"), // 0867 parentWidget.data()); 0868 label2->setBuddy(widget2); 0869 widget3->setFocus(); 0870 parentWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0871 parentWidget->show(); 0872 // The following statement make focus and widget events working. 0873 QApplication::setActiveWindow(parentWidget.data()); 0874 // Assert that the setup is okay. 0875 if (widget1->hasFocus()) { 0876 // Throw an exception instead of using an assert statement. 0877 // Assert statements seem to be not always reliably within QTest. 0878 throw 0; 0879 } 0880 if (widget2->hasFocus()) { 0881 // Throw an exception instead of using an assert statement. 0882 // Assert statements seem to be not always reliably within QTest. 0883 throw 0; 0884 } 0885 if (!widget3->hasFocus()) { 0886 // Throw an exception instead of using an assert statement. 0887 // Assert statements seem to be not always reliably within QTest. 0888 throw 0; 0889 } 0890 if (QApplication::focusWidget() != widget3) { 0891 // Throw an exception instead of using an assert statement. 0892 // Assert statements seem to be not always reliably within QTest. 0893 throw 0; 0894 } 0895 if (widget2->d_pointer->m_sectionConfigurations.count() != 3) { 0896 // Throw an exception instead of using an assert statement. 0897 // Assert statements seem to be not always reliably within QTest. 0898 throw 0; 0899 } 0900 0901 // Start actual testing 0902 // Move focus from widget3 to widget2/section2 0903 QTest::keyClick(QApplication::focusWidget(), // 0904 Qt::Key::Key_Tab, // 0905 Qt::KeyboardModifier::ShiftModifier); 0906 QCOMPARE(QApplication::focusWidget(), widget2); 0907 QCOMPARE(widget2->d_pointer->m_currentIndex, 2); 0908 // Move focus from widget2/section2 to widget2/section1 0909 QTest::keyClick(QApplication::focusWidget(), // 0910 Qt::Key::Key_Tab, // 0911 Qt::KeyboardModifier::ShiftModifier); 0912 QCOMPARE(QApplication::focusWidget(), widget2); 0913 QCOMPARE(widget2->d_pointer->m_currentIndex, 1); 0914 // Move focus from widget2/section1 to widget2/section0 0915 QTest::keyClick(QApplication::focusWidget(), // 0916 Qt::Key::Key_Tab, // 0917 Qt::KeyboardModifier::ShiftModifier); 0918 QCOMPARE(QApplication::focusWidget(), widget2); 0919 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 0920 // Move focus from widget2/section0 to widget1 0921 QTest::keyClick(QApplication::focusWidget(), // 0922 Qt::Key::Key_Tab, // 0923 Qt::KeyboardModifier::ShiftModifier); 0924 QCOMPARE(QApplication::focusWidget(), widget1); 0925 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 0926 } 0927 0928 void testFocusIntegrationIntegrationWithMnemonicBuddy() 0929 { 0930 // Integration test for: 0931 // → MultiSpinBox::focusNextPrevChild() 0932 // → MultiSpinBox::focusInEvent() 0933 // → MultiSpinBox::focusOutEvent() 0934 QScopedPointer<QWidget> parentWidget(new QWidget()); 0935 QSpinBox *widget1 = new QSpinBox(parentWidget.data()); 0936 widget1->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0937 PerceptualColor::MultiSpinBox *widget2 = // 0938 new PerceptualColor::MultiSpinBox(parentWidget.data()); 0939 widget2->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0940 widget2->setSectionConfigurations(exampleConfigurations); 0941 widget2->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(1); 0942 QSpinBox *widget3 = new QSpinBox(parentWidget.data()); 0943 widget3->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0944 QLabel *label2 = new QLabel(QStringLiteral(u"&Test"), // 0945 parentWidget.data()); 0946 label2->setBuddy(widget2); 0947 QLabel *label3 = new QLabel(QStringLiteral(u"&Other widget"), // 0948 parentWidget.data()); 0949 label3->setBuddy(widget3); 0950 widget3->setFocus(); 0951 parentWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 0952 parentWidget->show(); 0953 // The following statement make focus and widget events working. 0954 QApplication::setActiveWindow(parentWidget.data()); 0955 // Assert that the setup is okay. 0956 if (widget1->hasFocus()) { 0957 // Throw an exception instead of using an assert statement. 0958 // Assert statements seem to be not always reliably within QTest. 0959 throw 0; 0960 } 0961 if (widget2->hasFocus()) { 0962 // Throw an exception instead of using an assert statement. 0963 // Assert statements seem to be not always reliably within QTest. 0964 throw 0; 0965 } 0966 if (!widget3->hasFocus()) { 0967 // Throw an exception instead of using an assert statement. 0968 // Assert statements seem to be not always reliably within QTest. 0969 throw 0; 0970 } 0971 if (QApplication::focusWidget() != widget3) { 0972 // Throw an exception instead of using an assert statement. 0973 // Assert statements seem to be not always reliably within QTest. 0974 throw 0; 0975 } 0976 if (widget2->d_pointer->m_sectionConfigurations.count() != 3) { 0977 // Throw an exception instead of using an assert statement. 0978 // Assert statements seem to be not always reliably within QTest. 0979 throw 0; 0980 } 0981 if (widget2->d_pointer->m_currentIndex != 1) { 0982 // Throw an exception instead of using an assert statement. 0983 // Assert statements seem to be not always reliably within QTest. 0984 throw 0; 0985 } 0986 0987 // Start actual testing 0988 // Move focus from widget3 to widget2/section0 0989 QTest::keyClick(QApplication::focusWidget(), // 0990 Qt::Key::Key_T, // 0991 Qt::KeyboardModifier::AltModifier); 0992 QCOMPARE(QApplication::focusWidget(), widget2); 0993 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 0994 // Move focus from widget2/section0 to widget2/section1 0995 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 0996 QCOMPARE(QApplication::focusWidget(), widget2); 0997 QCOMPARE(widget2->d_pointer->m_currentIndex, 1); 0998 // Move focus from widget2/section1 to widget3 0999 QTest::keyClick(QApplication::focusWidget(), // 1000 Qt::Key::Key_O, // 1001 Qt::KeyboardModifier::AltModifier); 1002 QCOMPARE(QApplication::focusWidget(), widget3); 1003 // Move focus from widget3 to widget2/section0 1004 // This has to move to section0 (even if before this event, the last 1005 // selected section of widget2 was NOT section0. 1006 QTest::keyClick(QApplication::focusWidget(), // 1007 Qt::Key::Key_T, // 1008 Qt::KeyboardModifier::AltModifier); 1009 QCOMPARE(QApplication::focusWidget(), widget2); 1010 QCOMPARE(widget2->d_pointer->m_currentIndex, 0); 1011 } 1012 1013 void testFocusIntegrationFocusPolicy() 1014 { 1015 // Integration test for: 1016 // → MultiSpinBox::focusNextPrevChild() 1017 // → MultiSpinBox::focusInEvent() 1018 // → MultiSpinBox::focusOutEvent() 1019 QScopedPointer<QWidget> parentWidget(new QWidget()); 1020 QSpinBox *widget1 = new QSpinBox(parentWidget.data()); 1021 widget1->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1022 PerceptualColor::MultiSpinBox *widget2 = // 1023 new PerceptualColor::MultiSpinBox(parentWidget.data()); 1024 widget2->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1025 widget2->setSectionConfigurations(exampleConfigurations); 1026 QSpinBox *widget3 = new QSpinBox(parentWidget.data()); 1027 widget3->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1028 QLabel *label2 = new QLabel(QStringLiteral(u"&Test"), // 1029 parentWidget.data()); 1030 label2->setBuddy(widget2); 1031 QLabel *label3 = new QLabel(QStringLiteral(u"&Other widget"), // 1032 parentWidget.data()); 1033 label3->setBuddy(widget3); 1034 widget3->setFocus(); 1035 parentWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1036 parentWidget->show(); 1037 // The following statement make focus and widget events working. 1038 QApplication::setActiveWindow(parentWidget.data()); 1039 // Assert that the setup is okay. 1040 if (widget1->hasFocus()) { 1041 // Throw an exception instead of using an assert statement. 1042 // Assert statements seem to be not always reliably within QTest. 1043 throw 0; 1044 } 1045 if (widget2->hasFocus()) { 1046 // Throw an exception instead of using an assert statement. 1047 // Assert statements seem to be not always reliably within QTest. 1048 throw 0; 1049 } 1050 if (!widget3->hasFocus()) { 1051 // Throw an exception instead of using an assert statement. 1052 // Assert statements seem to be not always reliably within QTest. 1053 throw 0; 1054 } 1055 if (QApplication::focusWidget() != widget3) { 1056 // Throw an exception instead of using an assert statement. 1057 // Assert statements seem to be not always reliably within QTest. 1058 throw 0; 1059 } 1060 if (widget2->d_pointer->m_sectionConfigurations.count() != 3) { 1061 // Throw an exception instead of using an assert statement. 1062 // Assert statements seem to be not always reliably within QTest. 1063 throw 0; 1064 } 1065 1066 // Start actual testing 1067 // Make sure that MultiSpinBox does not react on incoming tab focus 1068 // events if the current focus policy does not allow tab focus. 1069 widget2->setFocusPolicy(Qt::FocusPolicy::ClickFocus); 1070 widget1->setFocus(); 1071 if (QApplication::focusWidget() != widget1) { 1072 // Throw an exception instead of using an assert statement. 1073 // Assert statements seem to be not always reliably within QTest. 1074 throw 0; 1075 } 1076 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 1077 QCOMPARE(QApplication::focusWidget(), widget3); 1078 widget2->setFocusPolicy(Qt::FocusPolicy::NoFocus); 1079 widget1->setFocus(); 1080 if (QApplication::focusWidget() != widget1) { 1081 // Throw an exception instead of using an assert statement. 1082 // Assert statements seem to be not always reliably within QTest. 1083 throw 0; 1084 } 1085 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Tab); 1086 QCOMPARE(QApplication::focusWidget(), widget3); 1087 } 1088 1089 void testStepBy() 1090 { 1091 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1092 new PerceptualColor::MultiSpinBox()); 1093 widget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1094 widget->setSectionConfigurations(exampleConfigurations); 1095 widget->d_pointer->setCurrentIndexWithoutUpdatingText(0); 1096 widget->stepBy(13); 1097 QCOMPARE(widget->sectionValues().at(0), 13); 1098 widget->d_pointer->setCurrentIndexWithoutUpdatingText(1); 1099 widget->stepBy(130); 1100 QCOMPARE(widget->sectionValues().at(1), 100); 1101 widget->d_pointer->setCurrentIndexWithoutUpdatingText(2); 1102 widget->stepBy(-260); 1103 QCOMPARE(widget->sectionValues().at(2), 0); 1104 } 1105 1106 void testStepUpDown() 1107 { 1108 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1109 new PerceptualColor::MultiSpinBox()); 1110 widget->setSectionConfigurations(exampleConfigurations); 1111 QCOMPARE(widget->sectionValues().at(0), 0); 1112 widget->stepUp(); 1113 QCOMPARE(widget->sectionValues().at(0), 1); 1114 widget->stepUp(); 1115 QCOMPARE(widget->sectionValues().at(0), 2); 1116 widget->stepDown(); 1117 QCOMPARE(widget->sectionValues().at(0), 1); 1118 widget->stepDown(); 1119 QCOMPARE(widget->sectionValues().at(0), 0); 1120 widget->stepDown(); 1121 QCOMPARE(widget->sectionValues().at(0), 0); 1122 } 1123 1124 void testUpdateValueFromText1() 1125 { 1126 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1127 new PerceptualColor::MultiSpinBox()); 1128 widget->setSectionConfigurations(exampleConfigurations); 1129 const quint8 sampleSectionNumber = 1; 1130 widget->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue( // 1131 sampleSectionNumber); 1132 // Assert that the setup is okay. 1133 if (widget->lineEdit()->text() != QStringLiteral(u"0° 0% 0")) { 1134 // Throw an exception instead of using an assert statement. 1135 // Assert statements seem to be not always reliably within QTest. 1136 throw 0; 1137 } 1138 widget->d_pointer->updateCurrentValueFromText( // 1139 QStringLiteral(u"0° 9% 0")); 1140 QCOMPARE(widget->sectionValues().at(sampleSectionNumber), 9); 1141 } 1142 1143 void testUpdateValueFromText2() 1144 { 1145 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1146 new PerceptualColor::MultiSpinBox()); 1147 QList<MultiSpinBoxSection> specialConfiguration = exampleConfigurations; 1148 const quint8 sampleSectionNumber = 1; 1149 const quint8 sampleValue = 5; 1150 widget->setSectionConfigurations(specialConfiguration); 1151 QList<double> myValues; 1152 while (myValues.count() < specialConfiguration.count()) { 1153 myValues.append(0); 1154 } 1155 myValues[sampleSectionNumber] = sampleValue; 1156 widget->setSectionValues(myValues); 1157 widget->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue( // 1158 sampleSectionNumber); 1159 // Assert that the setup is okay. 1160 if (widget->lineEdit()->text() != QStringLiteral(u"0° 5% 0")) { 1161 // Throw an exception instead of using an assert statement. 1162 // Assert statements seem to be not always reliably within QTest. 1163 throw 0; 1164 } 1165 if (widget->sectionValues().at(sampleSectionNumber) != sampleValue) { 1166 // Throw an exception instead of using an assert statement. 1167 // Assert statements seem to be not always reliably within QTest. 1168 throw 0; 1169 } 1170 // suppress warnings 1171 qInstallMessageHandler(voidMessageHandler); 1172 // Execute the tested function (with an invalid argument) 1173 widget->d_pointer->updateCurrentValueFromText( // 1174 QStringLiteral(u"abcdef")); 1175 // do not suppress warning for generating invalid QColor anymore 1176 qInstallMessageHandler(nullptr); 1177 // The original value should not have changed. 1178 QCOMPARE(widget->sectionValues().at(sampleSectionNumber), sampleValue); 1179 } 1180 1181 void testUpdateSectionFromCursorPosition() 1182 { 1183 // Setup 1184 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1185 new PerceptualColor::MultiSpinBox()); 1186 QList<MultiSpinBoxSection> specialConfiguration = exampleConfigurations; 1187 const quint8 sampleSectionNumber = 1; 1188 const quint8 sampleValue = 5; 1189 widget->setSectionConfigurations(specialConfiguration); 1190 QList<double> myValues; 1191 while (myValues.count() < specialConfiguration.count()) { 1192 myValues.append(0); 1193 } 1194 myValues[sampleSectionNumber] = sampleValue; 1195 widget->setSectionValues(myValues); 1196 widget->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue( // 1197 sampleSectionNumber); 1198 // Assert that the setup is okay. 1199 if (widget->lineEdit()->text() != QStringLiteral(u"0° 5% 0")) { 1200 // Throw an exception instead of using an assert statement. 1201 // Assert statements seem to be not always reliably within QTest. 1202 throw 0; 1203 } 1204 if (widget->sectionValues().at(sampleSectionNumber) != sampleValue) { 1205 // Throw an exception instead of using an assert statement. 1206 // Assert statements seem to be not always reliably within QTest. 1207 throw 0; 1208 } 1209 1210 // Do testing 1211 widget->lineEdit()->setCursorPosition(0); 1212 QCOMPARE(widget->d_pointer->m_currentIndex, 0); 1213 widget->lineEdit()->setCursorPosition(1); 1214 QCOMPARE(widget->d_pointer->m_currentIndex, 0); 1215 widget->lineEdit()->setCursorPosition(2); 1216 QCOMPARE(widget->d_pointer->m_currentIndex, 0); 1217 widget->lineEdit()->setCursorPosition(4); 1218 QCOMPARE(widget->d_pointer->m_currentIndex, 1); 1219 widget->lineEdit()->setCursorPosition(5); 1220 QCOMPARE(widget->d_pointer->m_currentIndex, 1); 1221 widget->lineEdit()->setCursorPosition(6); 1222 QCOMPARE(widget->d_pointer->m_currentIndex, 1); 1223 widget->lineEdit()->setCursorPosition(8); 1224 QCOMPARE(widget->d_pointer->m_currentIndex, 2); 1225 widget->lineEdit()->setCursorPosition(9); 1226 QCOMPARE(widget->d_pointer->m_currentIndex, 2); 1227 } 1228 1229 void testInitialLineEditValue() 1230 { 1231 // Setup 1232 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1233 new PerceptualColor::MultiSpinBox()); 1234 QList<MultiSpinBoxSection> specialConfiguration = exampleConfigurations; 1235 const quint8 sampleSectionNumber = 1; 1236 const quint8 sampleValue = 5; 1237 widget->setSectionConfigurations(specialConfiguration); 1238 QList<double> myValues; 1239 while (myValues.count() < specialConfiguration.count()) { 1240 myValues.append(0); 1241 } 1242 myValues[sampleSectionNumber] = sampleValue; 1243 widget->setSectionValues(myValues); 1244 // Assert that the initial content of the line edit is okay 1245 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"0° 5% 0")); 1246 } 1247 1248 void testLocalizationAndInternationalization() 1249 { 1250 // Setup 1251 QScopedPointer<PerceptualColor::MultiSpinBox> widget( // 1252 new PerceptualColor::MultiSpinBox()); 1253 QList<MultiSpinBoxSection> mySectionList; 1254 MultiSpinBoxSection mySection; 1255 mySection.setDecimals(1); 1256 mySection.setMinimum(0); 1257 mySection.setMaximum(100); 1258 mySectionList.append(mySection); 1259 widget->setSectionConfigurations(mySectionList); 1260 QList<double> myValues; 1261 myValues.append(50); 1262 widget->setSectionValues(myValues); 1263 1264 // Begin testing 1265 1266 widget->setLocale(QLocale::English); 1267 // Without calling update() or other functions, the new locale should 1268 // be applied on-the-fly. 1269 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"50.0")); 1270 1271 widget->setLocale(QLocale::German); 1272 // Without calling update() or other functions, the new locale should 1273 // be applied on-the-fly. 1274 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"50,0")); 1275 1276 widget->setLocale(QLocale::Bengali); 1277 // Without calling update() or other functions, the new locale should 1278 // be applied on-the-fly. 1279 QCOMPARE(widget->lineEdit()->text(), QStringLiteral(u"৫০.০")); 1280 } 1281 1282 void testArrowKeys() 1283 { 1284 QScopedPointer<QWidget> parentWidget(new QWidget()); 1285 PerceptualColor::MultiSpinBox *widget2 = // 1286 new PerceptualColor::MultiSpinBox(parentWidget.data()); 1287 widget2->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1288 widget2->setSectionConfigurations(exampleConfigurations); 1289 widget2->setFocus(); 1290 parentWidget->setFocusPolicy(Qt::FocusPolicy::StrongFocus); 1291 parentWidget->show(); 1292 widget2->d_pointer->setCurrentIndexAndUpdateTextAndSelectValue(1); 1293 // The following statement make focus and widget events working. 1294 QApplication::setActiveWindow(parentWidget.data()); 1295 // Assert that the setup is okay. 1296 if (!widget2->hasFocus()) { 1297 // Throw an exception instead of using an assert statement. 1298 // Assert statements seem to be not always reliably within QTest. 1299 throw 0; 1300 } 1301 if (QApplication::focusWidget() != widget2) { 1302 // Throw an exception instead of using an assert statement. 1303 // Assert statements seem to be not always reliably within QTest. 1304 throw 0; 1305 } 1306 if (widget2->d_pointer->m_sectionConfigurations.count() != 3) { 1307 // Throw an exception instead of using an assert statement. 1308 // Assert statements seem to be not always reliably within QTest. 1309 throw 0; 1310 } 1311 if (widget2->lineEdit()->text() != QStringLiteral(u"0° 0% 0")) { 1312 // Throw an exception instead of using an assert statement. 1313 // Assert statements seem to be not always reliably within QTest. 1314 throw 0; 1315 } 1316 1317 // Start actual testing 1318 QTest::keyClick(QApplication::focusWidget(), Qt::Key::Key_Up); 1319 QCOMPARE(widget2->sectionValues().at(1), 1); 1320 QCOMPARE(widget2->lineEdit()->text(), QStringLiteral(u"0° 1% 0")); 1321 } 1322 1323 void testSectionConfigurationDebug() 1324 { 1325 // suppress warnings 1326 qInstallMessageHandler(voidMessageHandler); 1327 // Test if QDebug support does not make a crash. 1328 qDebug() << MultiSpinBoxSection(); 1329 // do not suppress warning for generating invalid QColor anymore 1330 qInstallMessageHandler(nullptr); 1331 } 1332 1333 void testAddActionButton() 1334 { 1335 MultiSpinBox mySpinBox; 1336 int oldWidth = 0; 1337 QCOMPARE(mySpinBox.d_pointer->m_actionButtonCount, 0); 1338 oldWidth = mySpinBox.sizeHint().width(); 1339 mySpinBox.addActionButton( // 1340 new QAction(QStringLiteral(u"test"), &mySpinBox), // 1341 QLineEdit::ActionPosition::TrailingPosition); 1342 QCOMPARE(mySpinBox.d_pointer->m_actionButtonCount, 1); 1343 QVERIFY2(mySpinBox.sizeHint().width() > oldWidth, 1344 "Verify: After adding an action button, " 1345 "the size hint has a bigger width than before."); 1346 oldWidth = mySpinBox.sizeHint().width(); 1347 mySpinBox.addActionButton( // 1348 new QAction(QStringLiteral(u"test"), &mySpinBox), // 1349 QLineEdit::ActionPosition::TrailingPosition); 1350 QCOMPARE(mySpinBox.d_pointer->m_actionButtonCount, 2); 1351 QVERIFY2(mySpinBox.sizeHint().width() > oldWidth, 1352 "Verify: After adding an action button, " 1353 "the size hint has a bigger width than before."); 1354 } 1355 1356 void testFixSectionValue_data() 1357 { 1358 QTest::addColumn<double>("value"); 1359 QTest::addColumn<double>("expectedOnIsWrappigFalse"); 1360 QTest::addColumn<double>("expectedOnIsWrappigTrue"); 1361 1362 QTest::newRow(" -5") << -05. << 000. << 355.; 1363 QTest::newRow(" 0") << 000. << 000. << 000.; 1364 QTest::newRow(" 5") << 005. << 005. << 005.; 1365 QTest::newRow("355") << 355. << 355. << 355.; 1366 QTest::newRow("360") << 360. << 360. << 000.; 1367 QTest::newRow("365") << 365. << 360. << 005.; 1368 QTest::newRow("715") << 715. << 360. << 355.; 1369 QTest::newRow("720") << 720. << 360. << 000.; 1370 QTest::newRow("725") << 725. << 360. << 005.; 1371 } 1372 1373 void testFixSectionValue() 1374 { 1375 MultiSpinBox mySpinBox; 1376 1377 QFETCH(double, value); 1378 QFETCH(double, expectedOnIsWrappigFalse); 1379 QFETCH(double, expectedOnIsWrappigTrue); 1380 1381 MultiSpinBoxSection myConfiguration; 1382 myConfiguration.setMinimum(0); 1383 myConfiguration.setMaximum(360); 1384 myConfiguration.setWrapping(false); 1385 QList<MultiSpinBoxSection> myConfigurations; 1386 myConfigurations.append(myConfiguration); 1387 mySpinBox.setSectionConfigurations(myConfigurations); 1388 QList<double> myValues; 1389 myValues.append(value); 1390 mySpinBox.setSectionValues(myValues); 1391 QCOMPARE(mySpinBox.sectionValues().at(0), expectedOnIsWrappigFalse); 1392 1393 myConfiguration.setWrapping(true); 1394 myConfigurations.clear(); 1395 myConfigurations.append(myConfiguration); 1396 mySpinBox.setSectionConfigurations(myConfigurations); 1397 mySpinBox.setSectionValues(myValues); 1398 QCOMPARE(mySpinBox.sectionValues().at(0), expectedOnIsWrappigTrue); 1399 } 1400 1401 void testFixedSectionOther_data() 1402 { 1403 QTest::addColumn<double>("value"); 1404 QTest::addColumn<double>("expectedOnIsWrappigFalse"); 1405 QTest::addColumn<double>("expectedOnIsWrappigTrue"); 1406 1407 QTest::newRow("-25") << -25. << -20. << 335.; 1408 QTest::newRow("-20") << -20. << -20. << -20.; 1409 QTest::newRow("-15") << -15. << -15. << -15.; 1410 QTest::newRow("335") << 335. << 335. << 335.; 1411 QTest::newRow("340") << 340. << 340. << -20.; 1412 QTest::newRow("345") << 345. << 340. << -15.; 1413 QTest::newRow("695") << 695. << 340. << 335.; 1414 QTest::newRow("700") << 700. << 340. << -20.; 1415 QTest::newRow("705") << 705. << 340. << -15.; 1416 } 1417 1418 void testFixedSectionOther() 1419 { 1420 MultiSpinBox mySpinBox; 1421 1422 QFETCH(double, value); 1423 QFETCH(double, expectedOnIsWrappigFalse); 1424 QFETCH(double, expectedOnIsWrappigTrue); 1425 1426 MultiSpinBoxSection myConfiguration; 1427 myConfiguration.setMinimum(-20); 1428 myConfiguration.setMaximum(340); 1429 myConfiguration.setWrapping(false); 1430 QList<MultiSpinBoxSection> myConfigurations; 1431 myConfigurations.append(myConfiguration); 1432 mySpinBox.setSectionConfigurations(myConfigurations); 1433 QList<double> myValues; 1434 myValues.append(value); 1435 mySpinBox.setSectionValues(myValues); 1436 QCOMPARE(mySpinBox.sectionValues().at(0), expectedOnIsWrappigFalse); 1437 1438 myConfiguration.setWrapping(true); 1439 myConfigurations.clear(); 1440 myConfigurations.append(myConfiguration); 1441 mySpinBox.setSectionConfigurations(myConfigurations); 1442 mySpinBox.setSectionValues(myValues); 1443 QCOMPARE(mySpinBox.sectionValues().at(0), expectedOnIsWrappigTrue); 1444 } 1445 1446 void testValuesSetterAndConfigurationsSetter() 1447 { 1448 // Both, sectionValues() and sectionConfigurations() have a count() 1449 // that has to be identical. The count of sectionConfigurations() is 1450 // mandatory. Make sure that different setters let the count()s 1451 // in a correct state. Our reference for default values is 1452 // QDoubleSpinBox. 1453 MultiSpinBox myMulti; 1454 QDoubleSpinBox myDoubleSpinBox; 1455 QList<MultiSpinBoxSection> myConfigurations; 1456 QList<double> myValues; 1457 1458 // Section count should be 1 (by default): 1459 QCOMPARE(myMulti.sectionConfigurations().count(), 1); 1460 QCOMPARE(myMulti.sectionValues().count(), 1); 1461 // Control that sections has default value: 1462 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1463 1464 // Raise the section count to 3: 1465 myConfigurations.append(MultiSpinBoxSection()); 1466 myConfigurations.append(MultiSpinBoxSection()); 1467 myConfigurations.append(MultiSpinBoxSection()); 1468 myMulti.setSectionConfigurations(myConfigurations); 1469 // Control that all the new sections got the default value: 1470 QCOMPARE(myMulti.sectionValues().at(1), myDoubleSpinBox.value()); 1471 QCOMPARE(myMulti.sectionValues().at(2), myDoubleSpinBox.value()); 1472 1473 // Put specific values into each of the 3 sections: 1474 myValues.clear(); 1475 myValues.append(10); 1476 myValues.append(11); 1477 myValues.append(12); 1478 myValues.append(13); // Too many values for current configuration count 1479 myMulti.setSectionValues(myValues); 1480 // Assert that the values have been applied correctly 1481 QCOMPARE(myMulti.sectionValues().at(0), 10); 1482 QCOMPARE(myMulti.sectionValues().at(1), 11); 1483 QCOMPARE(myMulti.sectionValues().at(2), 12); 1484 // The last value has to be ignored (as there are not so many sections): 1485 QCOMPARE(myMulti.sectionConfigurations().count(), 3); 1486 QCOMPARE(myMulti.sectionValues().count(), 3); 1487 1488 // Apply a configuration with less sections 1489 myConfigurations.removeLast(); 1490 QCOMPARE(myConfigurations.count(), 2); // Assertion 1491 myMulti.setSectionConfigurations(myConfigurations); 1492 QCOMPARE(myMulti.sectionConfigurations().count(), 2); 1493 QCOMPARE(myMulti.sectionValues().count(), 2); 1494 // The values that survive should not be changed: 1495 QCOMPARE(myMulti.sectionValues().at(0), 10); 1496 QCOMPARE(myMulti.sectionValues().at(1), 11); 1497 1498 // Set sectionValues that has not enough values 1499 QCOMPARE(myMulti.sectionConfigurations().count(), 2); // Assertion 1500 QCOMPARE(myMulti.sectionValues().count(), 2); // Assertion 1501 QCOMPARE(myMulti.sectionValues().at(0), 10); // Assertion 1502 QCOMPARE(myMulti.sectionValues().at(1), 11); // Assertion 1503 myValues.clear(); 1504 myValues.append(20); 1505 // Apply a value list with only 1 value: 1506 myMulti.setSectionValues(myValues); 1507 QCOMPARE(myMulti.sectionValues().at(0), 20); // This values was applied 1508 // Section count has not been altered: 1509 QCOMPARE(myMulti.sectionConfigurations().count(), 2); 1510 QCOMPARE(myMulti.sectionValues().count(), 2); 1511 // The last section, that got no particular value assigned, 1512 // has been changed to the default value. (This behaviour 1513 // is not documented, so not part of the public API, but 1514 // is seems reasonable (and less confusing and more 1515 // predictable than just stay with the old value: 1516 QCOMPARE(myMulti.sectionValues().at(1), 0); 1517 } 1518 1519 void testSectionValuesChangedSignalBasic() 1520 { 1521 // Initialize 1522 MultiSpinBox myMulti; 1523 MultiSpinBoxSection myConfig; 1524 QList<MultiSpinBoxSection> myConfigs; 1525 myConfigs.append(myConfig); 1526 myConfigs.append(myConfig); 1527 myMulti.setSectionConfigurations(myConfigs); 1528 myMulti.show(); 1529 QSignalSpy spyMulti(&myMulti, // 1530 &MultiSpinBox::sectionValuesChanged); 1531 QDoubleSpinBox myDouble; 1532 myDouble.show(); 1533 QSignalSpy spyDouble( // 1534 &myDouble, // 1535 QOverload<double>::of(&QDoubleSpinBox::valueChanged)); 1536 1537 // Set a value different from the default 1538 myMulti.setSectionValues(QList<double>{2, 2}); 1539 myDouble.setValue(2); 1540 QCOMPARE(spyMulti.count(), 1); 1541 QCOMPARE(spyMulti.count(), spyDouble.count()); 1542 1543 // Setting the same value again should not call again the signal 1544 myMulti.setSectionValues(QList<double>{2, 2}); 1545 myDouble.setValue(2); 1546 QCOMPARE(spyMulti.count(), 1); 1547 QCOMPARE(spyMulti.count(), spyDouble.count()); 1548 1549 // Setting a value list which has only one different element triggers: 1550 myMulti.setSectionValues(QList<double>{2, 3}); 1551 myDouble.setValue(3); 1552 QCOMPARE(spyMulti.count(), 2); 1553 QCOMPARE(spyMulti.count(), spyDouble.count()); 1554 } 1555 1556 void testSectionValuesChangedSignalKeyboardTracking() 1557 { 1558 // Initialize 1559 MultiSpinBox myMulti; 1560 myMulti.setSectionConfigurations( 1561 // Use only one section to allow to compare easily 1562 // with QDoubleSpinBox 1563 QList<MultiSpinBoxSection>{MultiSpinBoxSection()}); 1564 myMulti.show(); 1565 QSignalSpy spyMulti(&myMulti, // 1566 &MultiSpinBox::sectionValuesChanged); 1567 QDoubleSpinBox myDouble; 1568 myDouble.show(); 1569 QSignalSpy spyDouble( // 1570 &myDouble, // 1571 QOverload<double>::of(&QDoubleSpinBox::valueChanged)); 1572 1573 // Test with keyboard tracking 1574 myMulti.setKeyboardTracking(true); 1575 myDouble.setKeyboardTracking(true); 1576 1577 // Get test data 1578 QApplication::setActiveWindow(&myMulti); 1579 QTest::keyClick(&myMulti, Qt::Key_Up); // Get text selection 1580 QTest::keyClick(&myMulti, Qt::Key::Key_5); 1581 QTest::keyClick(&myMulti, Qt::Key::Key_4); 1582 QCOMPARE(myMulti.sectionValues().at(0), 54); // Assertion 1583 1584 // Get reference data 1585 QApplication::setActiveWindow(&myDouble); 1586 QTest::keyClick(&myDouble, Qt::Key_Up); 1587 QTest::keyClick(&myDouble, Qt::Key::Key_5); 1588 QTest::keyClick(&myDouble, Qt::Key::Key_4); 1589 QCOMPARE(myDouble.value(), 54); // Assertion 1590 1591 // Test conformance of MultiSpinBox with QDoubleSpinBox’s behaviour 1592 QCOMPARE(spyMulti.count(), spyDouble.count()); 1593 for (int i = 0; i < spyMulti.count(); ++i) { 1594 QCOMPARE(spyMulti // Get value of first section of MultiSpinBox… 1595 .at(i) // Signal at position i 1596 .at(0) // First argument of this signal 1597 .value<QList<double>>() // Convert to original type 1598 .at(0), // First section of the MultiSpinBox 1599 spyDouble // Get value of QSpinBox… 1600 .at(i) // Signal at position i 1601 .at(0) // First argument of this signal 1602 .toDouble() // Convert to original type 1603 ); 1604 } 1605 } 1606 1607 void testRoundingBehaviourCompliance() 1608 { 1609 // Test the compliance of the behaviour of this class with 1610 // the behaviour of QDoubleSpinBox 1611 MultiSpinBoxSection myConfig; 1612 myConfig.setDecimals(0); 1613 myConfig.setMinimum(5); 1614 myConfig.setMaximum(360); 1615 MultiSpinBox myMulti; 1616 myMulti.setSectionConfigurations( 1617 // Create on-the-fly a list with only one section 1618 QList<MultiSpinBoxSection>{myConfig}); 1619 QDoubleSpinBox myDoubleSpinBox; 1620 myDoubleSpinBox.setDecimals(0); 1621 myDoubleSpinBox.setMinimum(5); 1622 myDoubleSpinBox.setMaximum(360); 1623 1624 myMulti.setSectionValues(QList<double>{-1}); 1625 myDoubleSpinBox.setValue(-1); 1626 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1627 1628 myMulti.setSectionValues(QList<double>{0}); 1629 myDoubleSpinBox.setValue(0); 1630 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1631 1632 // Test with a value that rounds down and stays too small 1633 myMulti.setSectionValues(QList<double>{4.1}); 1634 myDoubleSpinBox.setValue(4.1); 1635 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1636 1637 // Test with a value that is too small, but rounds up to the minimum 1638 myMulti.setSectionValues(QList<double>{4.9}); // Rounds up to 5 1639 myDoubleSpinBox.setValue(4.9); // Rounds up to 5 1640 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1641 1642 myMulti.setSectionValues(QList<double>{5}); 1643 myDoubleSpinBox.setValue(5); 1644 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1645 1646 // Test with a value that rounds down to the minimum 1647 myMulti.setSectionValues(QList<double>{5.1}); 1648 myDoubleSpinBox.setValue(5.1); 1649 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1650 1651 // Test with a value in the middle that rounds down 1652 myMulti.setSectionValues(QList<double>{72.1}); // Rounds up to 5 1653 myDoubleSpinBox.setValue(72.1); 1654 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1655 1656 // Test with a value in the middle that rounds up 1657 myMulti.setSectionValues(QList<double>{72.9}); // Rounds up to 5 1658 myDoubleSpinBox.setValue(72.9); 1659 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1660 1661 // Test with a value that is in range and rounds down 1662 myMulti.setSectionValues(QList<double>{359.1}); 1663 myDoubleSpinBox.setValue(359.1); 1664 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1665 1666 // Test with a value that rounds up to the maximum 1667 myMulti.setSectionValues(QList<double>{359.9}); 1668 myDoubleSpinBox.setValue(359.9); 1669 1670 // Test with maximum 1671 myMulti.setSectionValues(QList<double>{360}); 1672 myDoubleSpinBox.setValue(360); 1673 1674 // Test with value that rounds down to maximum 1675 myMulti.setSectionValues(QList<double>{360.1}); 1676 myDoubleSpinBox.setValue(360.1); 1677 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1678 1679 myMulti.setSectionValues(QList<double>{361}); 1680 myDoubleSpinBox.setValue(361); 1681 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1682 } 1683 1684 void testRoundingBehaviourComplianceWithRoundedRanges() 1685 { 1686 // Test the compliance of the behaviour of this class with 1687 // the behaviour of QDoubleSpinBox 1688 MultiSpinBoxSection myConfig; 1689 myConfig.setDecimals(0); 1690 myConfig.setMinimum(4.8); 1691 myConfig.setMaximum(360.2); 1692 MultiSpinBox myMulti; 1693 myMulti.setSectionConfigurations( 1694 // Create on-the-fly a list with only one section 1695 QList<MultiSpinBoxSection>{myConfig}); 1696 QDoubleSpinBox myDoubleSpinBox; 1697 myDoubleSpinBox.setDecimals(0); 1698 myDoubleSpinBox.setMinimum(4.8); 1699 myDoubleSpinBox.setMaximum(360.2); 1700 1701 myMulti.setSectionValues(QList<double>{-1}); 1702 myDoubleSpinBox.setValue(-1); 1703 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1704 1705 myMulti.setSectionValues(QList<double>{0}); 1706 myDoubleSpinBox.setValue(0); 1707 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1708 1709 // Test with a value that rounds down and stays too small 1710 myMulti.setSectionValues(QList<double>{4.1}); 1711 myDoubleSpinBox.setValue(4.1); 1712 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1713 1714 myMulti.setSectionValues(QList<double>{4.7}); 1715 myDoubleSpinBox.setValue(4.7); // Rounds up to 5 1716 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1717 1718 myMulti.setSectionValues(QList<double>{4.8}); 1719 myDoubleSpinBox.setValue(4.8); // Rounds up to 5 1720 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1721 1722 myMulti.setSectionValues(QList<double>{4.9}); 1723 myDoubleSpinBox.setValue(4.9); // Rounds up to 5 1724 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1725 1726 myMulti.setSectionValues(QList<double>{5}); 1727 myDoubleSpinBox.setValue(5); 1728 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1729 1730 myMulti.setSectionValues(QList<double>{5.1}); // Rounds up to 5 1731 myDoubleSpinBox.setValue(5.1); // Rounds up to 5 1732 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1733 1734 myMulti.setSectionValues(QList<double>{72.1}); // Rounds up to 5 1735 myDoubleSpinBox.setValue(72.1); 1736 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1737 1738 myMulti.setSectionValues(QList<double>{72.9}); // Rounds up to 5 1739 myDoubleSpinBox.setValue(72.9); 1740 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1741 1742 myMulti.setSectionValues(QList<double>{359.1}); 1743 myDoubleSpinBox.setValue(359.1); 1744 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1745 1746 myMulti.setSectionValues(QList<double>{359.9}); 1747 myDoubleSpinBox.setValue(359.9); 1748 1749 myMulti.setSectionValues(QList<double>{360}); 1750 myDoubleSpinBox.setValue(360); 1751 1752 myMulti.setSectionValues(QList<double>{360.1}); 1753 myDoubleSpinBox.setValue(360.1); 1754 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1755 1756 myMulti.setSectionValues(QList<double>{360.2}); 1757 myDoubleSpinBox.setValue(360.2); 1758 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1759 1760 myMulti.setSectionValues(QList<double>{360.3}); 1761 myDoubleSpinBox.setValue(360.3); 1762 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1763 1764 myMulti.setSectionValues(QList<double>{360.9}); 1765 myDoubleSpinBox.setValue(360.9); 1766 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1767 1768 myMulti.setSectionValues(QList<double>{361}); 1769 myDoubleSpinBox.setValue(361); 1770 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1771 } 1772 1773 void testRoundingBehaviourCornerCases() 1774 { 1775 // Test the compliance of the behaviour of this class with 1776 // the behaviour of QDoubleSpinBox 1777 MultiSpinBoxSection myConfig; 1778 myConfig.setDecimals(0); 1779 myConfig.setMinimum(4.8); 1780 myConfig.setMaximum(359.8); 1781 MultiSpinBox myMulti; 1782 myMulti.setSectionConfigurations( 1783 // Create on-the-fly a list with only one section 1784 QList<MultiSpinBoxSection>{myConfig}); 1785 QDoubleSpinBox myDoubleSpinBox; 1786 myDoubleSpinBox.setDecimals(0); 1787 myDoubleSpinBox.setMinimum(4.8); 1788 myDoubleSpinBox.setMaximum(359.8); 1789 1790 myMulti.setSectionValues(QList<double>{359}); 1791 myDoubleSpinBox.setValue(359); 1792 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1793 1794 myMulti.setSectionValues(QList<double>{359.7}); 1795 myDoubleSpinBox.setValue(359.7); 1796 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1797 1798 myMulti.setSectionValues(QList<double>{359.8}); 1799 myDoubleSpinBox.setValue(359.8); 1800 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1801 1802 myMulti.setSectionValues(QList<double>{359.9}); 1803 myDoubleSpinBox.setValue(359.9); 1804 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1805 1806 myMulti.setSectionValues(QList<double>{360}); 1807 myDoubleSpinBox.setValue(360); 1808 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1809 } 1810 1811 void testRoundingAfterChangingDecimals() 1812 { 1813 // Test the compliance of the behaviour of this class with 1814 // the behaviour of QDoubleSpinBox 1815 QList<MultiSpinBoxSection> myConfigs 1816 // Initialize the list with one single, default section 1817 {MultiSpinBoxSection()}; 1818 myConfigs[0].setDecimals(2); 1819 MultiSpinBox myMulti; 1820 myMulti.setSectionConfigurations(myConfigs); 1821 QDoubleSpinBox myDoubleSpinBox; 1822 myDoubleSpinBox.setDecimals(2); 1823 const double initialTestValue = 12.34; 1824 myMulti.setSectionValues(QList<double>{initialTestValue}); 1825 myDoubleSpinBox.setValue(initialTestValue); 1826 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1827 1828 myConfigs[0].setDecimals(1); 1829 myMulti.setSectionConfigurations(myConfigs); 1830 myDoubleSpinBox.setDecimals(1); 1831 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1832 1833 myConfigs[0].setDecimals(0); 1834 myMulti.setSectionConfigurations(myConfigs); 1835 myDoubleSpinBox.setDecimals(0); 1836 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1837 1838 myConfigs[0].setDecimals(3); 1839 myMulti.setSectionConfigurations(myConfigs); 1840 myDoubleSpinBox.setDecimals(3); 1841 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1842 1843 myConfigs[0].setDecimals(-1); 1844 myMulti.setSectionConfigurations(myConfigs); 1845 myDoubleSpinBox.setDecimals(-1); 1846 QCOMPARE(myMulti.sectionValues().at(0), myDoubleSpinBox.value()); 1847 } 1848 1849 void testMaximumWrappingRounding_data() 1850 { 1851 QTest::addColumn<double>("value"); 1852 1853 QTest::newRow("-360.1") << -360.1; 1854 QTest::newRow("-360") << -360.0; 1855 QTest::newRow("-359.9") << -359.9; 1856 QTest::newRow("-0.1") << -0.1; 1857 QTest::newRow("0") << 0.0; 1858 QTest::newRow("0.1") << 0.1; 1859 QTest::newRow("359.9") << 359.9; 1860 QTest::newRow("360") << 360.0; 1861 QTest::newRow("360.1") << 360.1; 1862 QTest::newRow("719.9") << 719.9; 1863 QTest::newRow("720") << 720.0; 1864 QTest::newRow("720.1") << 720.1; 1865 } 1866 1867 void testMaximumWrappingRounding() 1868 { 1869 // When using wrapping, the MultiSpinBox is supposed to never 1870 // show “360”, but instead “0”. This should also be true when 1871 // rounding applies. And when being a magnitude higher or lower. 1872 1873 // Get data 1874 QFETCH(double, value); 1875 1876 // Initialization 1877 MultiSpinBoxSection myConfig; 1878 myConfig.setDecimals(0); 1879 myConfig.setMinimum(0); 1880 myConfig.setMaximum(360); 1881 myConfig.setWrapping(true); 1882 MultiSpinBox mySpinBox; 1883 mySpinBox.setSectionConfigurations( 1884 // Create the QList on the fly… 1885 QList<MultiSpinBoxSection>{myConfig}); 1886 1887 mySpinBox.setSectionValues( 1888 // Create the QList on the fly… 1889 QList<double>{value}); 1890 QCOMPARE(mySpinBox.text(), QStringLiteral("0")); 1891 1892 mySpinBox.setSectionValues( 1893 // Create the QList on the fly… 1894 QList<double>{359.9}); 1895 QCOMPARE(mySpinBox.text(), QStringLiteral("0")); 1896 } 1897 1898 void testMetaTypeDeclaration() 1899 { 1900 QVariant test; 1901 // The next line should produce a compiler error if the 1902 // type is not declared to Qt’s Meta Object System. 1903 test.setValue(MultiSpinBoxSection()); 1904 } 1905 1906 void testMetaTypeDeclarationForPropertySectionValues() 1907 { 1908 // The data type QList<double> seems to be automatically declared 1909 // because it’s an instance of QList. This unit test controls this 1910 // assumption. 1911 QVariant test; 1912 // The next line should produce a compiler error if the 1913 // type is not declared to Qt’s Meta Object System. 1914 test.setValue(QList<double>()); 1915 } 1916 1917 void testSnippet02() 1918 { 1919 snippet02(); 1920 } 1921 }; 1922 1923 } // namespace PerceptualColor 1924 1925 QTEST_MAIN(PerceptualColor::TestMultiSpinBox) 1926 1927 // The following “include” is necessary because we do not use a header file: 1928 #include "testmultispinbox.moc"