1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qapplication.h>
32#include <limits.h>
33
34#include <cmath>
35#include <float.h>
36
37#include <qspinbox.h>
38#include <qlocale.h>
39#include <qlayout.h>
40
41#include <qlineedit.h>
42#include <qdebug.h>
43
44#include <QStyleOptionSpinBox>
45#include <QStyle>
46#include <QProxyStyle>
47
48class DoubleSpinBox : public QDoubleSpinBox
49{
50 Q_OBJECT
51public:
52 DoubleSpinBox(QWidget *parent = 0)
53 : QDoubleSpinBox(parent)
54 { /*connect(this, SIGNAL(valueChanged(double)), this, SLOT(foo(double)));*/ }
55 QString textFromValue(double v) const
56 {
57 return QDoubleSpinBox::textFromValue(val: v);
58 }
59 QValidator::State validate(QString &text, int &pos) const
60 {
61 return QDoubleSpinBox::validate(input&: text, pos);
62 }
63 double valueFromText(const QString &text) const
64 {
65 return QDoubleSpinBox::valueFromText(text);
66 }
67#if QT_CONFIG(wheelevent)
68 void wheelEvent(QWheelEvent *event)
69 {
70 QDoubleSpinBox::wheelEvent(event);
71 }
72#endif
73 void initStyleOption(QStyleOptionSpinBox *option) const
74 {
75 QDoubleSpinBox::initStyleOption(option);
76 }
77
78 QLineEdit* lineEdit() const { return QDoubleSpinBox::lineEdit(); }
79public slots:
80 void foo(double vla)
81 {
82 qDebug() << vla;
83 }
84};
85
86class PressAndHoldStyle : public QProxyStyle
87{
88 Q_OBJECT
89public:
90 using QProxyStyle::QProxyStyle;
91
92 int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr,
93 const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
94 {
95 switch (hint) {
96 case QStyle::SH_SpinBox_ClickAutoRepeatRate:
97 return 5;
98 case QStyle::SH_SpinBox_ClickAutoRepeatThreshold:
99 return 10;
100 default:
101 return QProxyStyle::styleHint(hint, option, widget, returnData);
102 }
103 }
104};
105
106class StepModifierStyle : public QProxyStyle
107{
108 Q_OBJECT
109public:
110 using QProxyStyle::QProxyStyle;
111
112 int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr,
113 const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
114 {
115 switch (hint) {
116 case QStyle::SH_SpinBox_StepModifier:
117 return stepModifier;
118 default:
119 return QProxyStyle::styleHint(hint, option, widget, returnData);
120 }
121 }
122
123 Qt::KeyboardModifier stepModifier = Qt::ControlModifier;
124};
125
126
127class tst_QDoubleSpinBox : public QObject
128{
129 Q_OBJECT
130public:
131 tst_QDoubleSpinBox();
132 virtual ~tst_QDoubleSpinBox();
133public slots:
134 void initTestCase();
135 void cleanupTestCase();
136 void init();
137
138private slots:
139 void germanTest();
140
141 void task54433();
142
143 void setValue_data();
144 void setValue();
145
146 void setPrefixSuffix_data();
147 void setPrefixSuffix();
148
149 void setTracking_data();
150 void setTracking();
151
152 void setWrapping_data();
153 void setWrapping();
154
155 void setSpecialValueText_data();
156 void setSpecialValueText();
157
158 void setSingleStep_data();
159 void setSingleStep();
160
161 void setMinMax_data();
162 void setMinMax();
163
164 void setDecimals_data();
165 void setDecimals();
166
167 void doubleDot();
168
169 void undoRedo();
170
171 void valueFromTextAndValidate_data();
172 void valueFromTextAndValidate();
173
174 void setReadOnly();
175
176 void editingFinished();
177
178 void removeAll();
179
180 void task199226_stateAfterEnter();
181 void task224497_fltMax();
182
183 void task221221();
184 void task255471_decimalsValidation();
185
186 void taskQTBUG_5008_textFromValueAndValidate();
187 void taskQTBUG_6670_selectAllWithPrefix();
188 void taskQTBUG_6496_fiddlingWithPrecision();
189
190 void setGroupSeparatorShown_data();
191 void setGroupSeparatorShown();
192
193 void adaptiveDecimalStep();
194
195 void wheelEvents_data();
196 void wheelEvents();
197
198 void stepModifierKeys_data();
199 void stepModifierKeys();
200
201 void stepModifierButtons_data();
202 void stepModifierButtons();
203
204 void stepModifierPressAndHold_data();
205 void stepModifierPressAndHold();
206public slots:
207 void valueChangedHelper(const QString &);
208 void valueChangedHelper(double);
209private:
210 QStringList actualTexts;
211 QList<double> actualValues;
212 QWidget *testFocusWidget;
213};
214
215typedef QList<double> DoubleList;
216
217Q_DECLARE_METATYPE(QLocale::Language)
218Q_DECLARE_METATYPE(QLocale::Country)
219
220static QLatin1String modifierToName(Qt::KeyboardModifier modifier)
221{
222 switch (modifier) {
223 case Qt::NoModifier:
224 return QLatin1String("No");
225 break;
226 case Qt::ControlModifier:
227 return QLatin1String("Ctrl");
228 break;
229 case Qt::ShiftModifier:
230 return QLatin1String("Shift");
231 break;
232 case Qt::AltModifier:
233 return QLatin1String("Alt");
234 break;
235 case Qt::MetaModifier:
236 return QLatin1String("Meta");
237 break;
238 default:
239 qFatal(msg: "Unexpected keyboard modifier");
240 return QLatin1String();
241 }
242}
243
244tst_QDoubleSpinBox::tst_QDoubleSpinBox()
245
246{
247}
248
249tst_QDoubleSpinBox::~tst_QDoubleSpinBox()
250{
251
252}
253
254void tst_QDoubleSpinBox::initTestCase()
255{
256 testFocusWidget = new QWidget(0);
257 testFocusWidget->resize(w: 200, h: 100);
258 testFocusWidget->show();
259
260 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
261 QSKIP("Wayland: This fails. Figure out why.");
262
263 QVERIFY(QTest::qWaitForWindowActive(testFocusWidget));
264}
265
266void tst_QDoubleSpinBox::cleanupTestCase()
267{
268 delete testFocusWidget;
269 testFocusWidget = 0;
270}
271
272void tst_QDoubleSpinBox::init()
273{
274 QLocale::setDefault(QLocale(QLocale::C));
275}
276
277void tst_QDoubleSpinBox::setValue_data()
278{
279 QTest::addColumn<double>(name: "val");
280
281 QTest::newRow(dataTag: "data0") << 0.0;
282 QTest::newRow(dataTag: "data1") << 100.5;
283 QTest::newRow(dataTag: "data2") << -100.5;
284 QTest::newRow(dataTag: "data3") << -DBL_MAX;
285 QTest::newRow(dataTag: "data4") << DBL_MAX;
286}
287
288void tst_QDoubleSpinBox::setValue()
289{
290 QFETCH(double, val);
291 QDoubleSpinBox spin(0);
292 spin.setRange(min: -DBL_MAX, DBL_MAX);
293 spin.setValue(val);
294 QCOMPARE(spin.value(), val);
295}
296
297void tst_QDoubleSpinBox::setPrefixSuffix_data()
298{
299 QTest::addColumn<QString>(name: "prefix");
300 QTest::addColumn<QString>(name: "suffix");
301 QTest::addColumn<double>(name: "value");
302 QTest::addColumn<int>(name: "decimals");
303 QTest::addColumn<QString>(name: "expectedText");
304 QTest::addColumn<QString>(name: "expectedCleanText");
305 QTest::addColumn<bool>(name: "show");
306
307 QTest::newRow(dataTag: "data0") << QString() << QString() << 10.5 << 1 << "10.5" << "10.5" << false;
308 QTest::newRow(dataTag: "data1") << QString() << "cm" << 10.5 << 2 << "10.50cm" << "10.50" << false;
309
310 // 10.5 is rounded correctly to 11 when using libdouble-conversion, or incorrectly to 10 when
311 // using snprintf. This is not the point of this test, though.
312 QTest::newRow(dataTag: "data2") << "cm: " << QString() << 10.4 << 0 << "cm: 10" << "10" << false;
313 QTest::newRow(dataTag: "data3") << "length: " << "cm" << 10.5 << 3 << "length: 10.500cm" << "10.500" << false;
314
315 QTest::newRow(dataTag: "data4") << QString() << QString() << 10.5 << 1 << "10.5" << "10.5" << true;
316 QTest::newRow(dataTag: "data5") << QString() << "cm" << 10.5 << 2 << "10.50cm" << "10.50" << true;
317 QTest::newRow(dataTag: "data6") << "cm: " << QString() << 10.4 << 0 << "cm: 10" << "10" << true;
318 QTest::newRow(dataTag: "data7") << "length: " << "cm" << 10.5 << 3 << "length: 10.500cm" << "10.500" << true;
319}
320
321void tst_QDoubleSpinBox::setPrefixSuffix()
322{
323 QFETCH(QString, prefix);
324 QFETCH(QString, suffix);
325 QFETCH(double, value);
326 QFETCH(int, decimals);
327 QFETCH(QString, expectedText);
328 QFETCH(QString, expectedCleanText);
329 QFETCH(bool, show);
330
331 QDoubleSpinBox spin;
332 if (show)
333 spin.show();
334 spin.setDecimals(decimals);
335 const QSize size1 = spin.sizeHint();
336 spin.setPrefix(prefix);
337 const QSize size2 = spin.sizeHint();
338 spin.setSuffix(suffix);
339 const QSize size3 = spin.sizeHint();
340 spin.setValue(value);
341
342 QCOMPARE(spin.text(), expectedText);
343 QCOMPARE(spin.cleanText(), expectedCleanText);
344
345 if (!suffix.isEmpty()) {
346 QVERIFY(size2.width() < size3.width());
347 spin.setSuffix(QString());
348 QCOMPARE(spin.sizeHint(), size2);
349 }
350 if (!prefix.isEmpty()) {
351 QVERIFY(size1.width() < size2.width());
352 spin.setPrefix(QString());
353 QCOMPARE(spin.sizeHint(), size1);
354 }
355}
356
357void tst_QDoubleSpinBox::valueChangedHelper(const QString &text)
358{
359 actualTexts << text;
360}
361
362void tst_QDoubleSpinBox::valueChangedHelper(double value)
363{
364 actualValues << value;
365}
366
367void tst_QDoubleSpinBox::setTracking_data()
368{
369 QTest::addColumn<int>(name: "decimals");
370 QTest::addColumn<QTestEventList>(name: "keys");
371 QTest::addColumn<QStringList>(name: "texts");
372 QTest::addColumn<bool>(name: "tracking");
373
374 QTestEventList keys;
375 QStringList texts1;
376 QStringList texts2;
377#ifdef Q_OS_MAC
378 keys.addKeyClick(Qt::Key_Right, Qt::ControlModifier);
379#else
380 keys.addKeyClick(qtKey: Qt::Key_End);
381#endif
382 keys.addKeyClick(qtKey: Qt::Key_Backspace);
383 keys.addKeyClick(qtKey: Qt::Key_Backspace);
384 keys.addKeyClick(qtKey: Qt::Key_Backspace);
385 keys.addKeyClick(qtKey: Qt::Key_Backspace);
386 keys.addKeyClick(qtKey: Qt::Key_Backspace);
387 keys.addKeyClick(qtKey: Qt::Key_Backspace);
388 keys.addKeyClick(qtKey: Qt::Key_Backspace);
389 keys.addKeyClick(qtKey: Qt::Key_Backspace);
390 keys.addKeyClick(ascii: '7');
391 keys.addKeyClick(ascii: '.');
392 keys.addKeyClick(ascii: '9');
393 keys.addKeyClick(qtKey: Qt::Key_Backspace);
394 keys.addKeyClick(ascii: '2');
395 keys.addKeyClick(qtKey: Qt::Key_Enter);
396 keys.addKeyClick(qtKey: Qt::Key_Enter);
397 keys.addKeyClick(qtKey: Qt::Key_Enter);
398 texts1 << "7" << "7.9" << "7." << "7.2" << "7.200" << "7.200" << "7.200";
399 texts2 << "7.200";
400 QTest::newRow(dataTag: "data1") << 3 << keys << texts1 << true;
401 QTest::newRow(dataTag: "data2") << 3 << keys << texts2 << false;
402
403}
404
405void tst_QDoubleSpinBox::setTracking()
406{
407 QLocale::setDefault(QLocale(QLocale::C));
408
409 actualTexts.clear();
410 QFETCH(int, decimals);
411 QFETCH(QTestEventList, keys);
412 QFETCH(QStringList, texts);
413 QFETCH(bool, tracking);
414
415 QDoubleSpinBox spin(0);
416 spin.setKeyboardTracking(tracking);
417 spin.setDecimals(decimals);
418 spin.show();
419
420 connect(sender: &spin, SIGNAL(valueChanged(QString)), receiver: this, SLOT(valueChangedHelper(QString)));
421
422 keys.simulate(w: &spin);
423 QCOMPARE(actualTexts, texts);
424}
425
426void tst_QDoubleSpinBox::setWrapping_data()
427{
428 QTest::addColumn<bool>(name: "wrapping");
429 QTest::addColumn<double>(name: "minimum");
430 QTest::addColumn<double>(name: "maximum");
431 QTest::addColumn<double>(name: "startValue");
432 QTest::addColumn<QTestEventList>(name: "keys");
433 QTest::addColumn<DoubleList>(name: "expected");
434
435 QTestEventList keys;
436 DoubleList values;
437 keys.addKeyClick(qtKey: Qt::Key_Up);
438 values << 10;
439 keys.addKeyClick(qtKey: Qt::Key_Up);
440 QTest::newRow(dataTag: "data0") << false << 0.0 << 10.0 << 9.0 << keys << values;
441
442 keys.clear();
443 values.clear();
444 keys.addKeyClick(qtKey: Qt::Key_Up);
445 values << 10;
446 keys.addKeyClick(qtKey: Qt::Key_Up);
447 values << 0;
448 QTest::newRow(dataTag: "data1") << true << 0.0 << 10.0 << 9.0 << keys << values;
449
450 keys.clear();
451 values.clear();
452#ifdef Q_OS_MAC
453 keys.addKeyClick(Qt::Key_Right, Qt::ControlModifier);
454#else
455 keys.addKeyClick(qtKey: Qt::Key_End);
456#endif
457 keys.addKeyClick(qtKey: Qt::Key_Backspace);
458 keys.addKeyClick(ascii: '1');
459 keys.addKeyClick(qtKey: Qt::Key_Down);
460 keys.addKeyClick(qtKey: Qt::Key_Down);
461 keys.addKeyClick(qtKey: Qt::Key_PageDown);
462 values << 9.01 << 8.01 << 7.01 << 0.0;
463 QTest::newRow(dataTag: "data2") << false << 0.0 << 10.0 << 9.0 << keys << values;
464
465 keys.clear();
466 values.clear();
467#ifdef Q_OS_MAC
468 keys.addKeyClick(Qt::Key_Left, Qt::ControlModifier);
469#else
470 keys.addKeyClick(qtKey: Qt::Key_Home);
471#endif
472 keys.addKeyClick(qtKey: Qt::Key_Delete);
473 keys.addKeyClick(qtKey: Qt::Key_Delete);
474 keys.addKeyClick(qtKey: Qt::Key_Delete);
475 keys.addKeyClick(qtKey: Qt::Key_Delete);
476 keys.addKeyClick(qtKey: Qt::Key_Delete);
477 keys.addKeyClick(qtKey: Qt::Key_Delete);
478 keys.addKeyClick(qtKey: Qt::Key_Delete);
479 keys.addKeyClick(ascii: '1');
480 keys.addKeyClick(qtKey: Qt::Key_Down);
481 keys.addKeyClick(qtKey: Qt::Key_Down);
482 values << 0 << 1 << 0 << 10;
483 QTest::newRow(dataTag: "data3") << true << 0.0 << 10.0 << 9.0 << keys << values;
484
485 keys.clear();
486 values.clear();
487 keys.addKeyClick(qtKey: Qt::Key_PageDown);
488 keys.addKeyClick(qtKey: Qt::Key_Down);
489 values << 0;
490 QTest::newRow(dataTag: "data4") << false << 0.0 << 10.0 << 6.0 << keys << values;
491
492 keys.clear();
493 values.clear();
494 keys.addKeyClick(qtKey: Qt::Key_PageDown);
495 keys.addKeyClick(qtKey: Qt::Key_Down);
496 values << 0 << 10;
497 QTest::newRow(dataTag: "data5") << true << 0.0 << 10.0 << 6.0 << keys << values;
498
499 keys.clear();
500 values.clear();
501 keys.addKeyClick(qtKey: Qt::Key_PageUp);
502 keys.addKeyClick(qtKey: Qt::Key_PageDown);
503 keys.addKeyClick(qtKey: Qt::Key_Down);
504 keys.addKeyClick(qtKey: Qt::Key_Up);
505 keys.addKeyClick(qtKey: Qt::Key_PageDown);
506 keys.addKeyClick(qtKey: Qt::Key_PageDown);
507 values << 10 << 0 << 10 << 0 << 10 << 0;
508 QTest::newRow(dataTag: "data6") << true << 0.0 << 10.0 << 6.0 << keys << values;
509
510}
511
512
513void tst_QDoubleSpinBox::setWrapping()
514{
515 QLocale::setDefault(QLocale(QLocale::C));
516 QFETCH(bool, wrapping);
517 QFETCH(double, minimum);
518 QFETCH(double, maximum);
519 QFETCH(double, startValue);
520 QFETCH(QTestEventList, keys);
521 QFETCH(DoubleList, expected);
522
523 QDoubleSpinBox spin(0);
524 spin.setMinimum(minimum);
525 spin.setMaximum(maximum);
526 spin.setValue(startValue);
527 spin.setWrapping(wrapping);
528 spin.show();
529 actualValues.clear();
530 connect(sender: &spin, SIGNAL(valueChanged(double)), receiver: this, SLOT(valueChangedHelper(double)));
531
532 keys.simulate(w: &spin);
533
534 QCOMPARE(actualValues.size(), expected.size());
535 for (int i=0; i<qMin(a: actualValues.size(), b: expected.size()); ++i) {
536 QCOMPARE(actualValues.at(i), expected.at(i));
537 }
538}
539
540void tst_QDoubleSpinBox::setSpecialValueText_data()
541{
542 QTest::addColumn<QString>(name: "specialValueText");
543 QTest::addColumn<double>(name: "minimum");
544 QTest::addColumn<double>(name: "maximum");
545 QTest::addColumn<double>(name: "value");
546 QTest::addColumn<int>(name: "decimals");
547 QTest::addColumn<QString>(name: "expected");
548 QTest::addColumn<bool>(name: "show");
549
550 QTest::newRow(dataTag: "data0") << QString() << 0.0 << 10.0 << 1.0 << 4 << "1.0000" << false;
551 QTest::newRow(dataTag: "data1") << QString() << 0.0 << 10.0 << 1.0 << 4 << "1.0000" << true;
552 QTest::newRow(dataTag: "data2") << "foo" << 0.0 << 10.0 << 0.0 << 2 << "foo" << false;
553 QTest::newRow(dataTag: "data3") << "foo" << 0.0 << 10.0 << 0.0 << 2 << "foo" << true;
554}
555
556void tst_QDoubleSpinBox::setSpecialValueText()
557{
558 QFETCH(QString, specialValueText);
559 QFETCH(double, minimum);
560 QFETCH(double, maximum);
561 QFETCH(double, value);
562 QFETCH(int, decimals);
563 QFETCH(QString, expected);
564 QFETCH(bool, show);
565
566 QDoubleSpinBox spin(0);
567 spin.setSpecialValueText(specialValueText);
568 spin.setMinimum(minimum);
569 spin.setMaximum(maximum);
570 spin.setValue(value);
571 spin.setDecimals(decimals);
572 if (show)
573 spin.show();
574
575 QCOMPARE(spin.text(), expected);
576}
577
578void tst_QDoubleSpinBox::setSingleStep_data()
579{
580 QTest::addColumn<double>(name: "singleStep");
581 QTest::addColumn<double>(name: "startValue");
582 QTest::addColumn<QTestEventList>(name: "keys");
583 QTest::addColumn<DoubleList>(name: "expected");
584 QTest::addColumn<bool>(name: "show");
585
586 QTestEventList keys;
587 DoubleList values;
588 keys.addKeyClick(qtKey: Qt::Key_Up);
589 keys.addKeyClick(qtKey: Qt::Key_Down);
590 keys.addKeyClick(qtKey: Qt::Key_Up);
591 values << 11 << 10 << 11;
592 QTest::newRow(dataTag: "data0") << 1.0 << 10.0 << keys << values << false;
593 QTest::newRow(dataTag: "data1") << 1.0 << 10.0 << keys << values << true;
594
595 keys.clear();
596 values.clear();
597 keys.addKeyClick(qtKey: Qt::Key_Up);
598 keys.addKeyClick(qtKey: Qt::Key_Down);
599 keys.addKeyClick(qtKey: Qt::Key_Up);
600 values << 12.5 << 10.0 << 12.5;
601 QTest::newRow(dataTag: "data2") << 2.5 << 10.0 << keys << values << false;
602 QTest::newRow(dataTag: "data3") << 2.5 << 10.0 << keys << values << true;
603}
604
605void tst_QDoubleSpinBox::setSingleStep()
606{
607 QFETCH(double, singleStep);
608 QFETCH(double, startValue);
609 QFETCH(QTestEventList, keys);
610 QFETCH(DoubleList, expected);
611 QFETCH(bool, show);
612
613 QDoubleSpinBox spin(0);
614 actualValues.clear();
615 spin.setSingleStep(singleStep);
616 spin.setValue(startValue);
617 if (show)
618 spin.show();
619 connect(sender: &spin, SIGNAL(valueChanged(double)), receiver: this, SLOT(valueChangedHelper(double)));
620
621 QCOMPARE(actualValues.size(), 0);
622 keys.simulate(w: &spin);
623 QCOMPARE(actualValues.size(), expected.size());
624 for (int i=0; i<qMin(a: actualValues.size(), b: expected.size()); ++i) {
625 QCOMPARE(actualValues.at(i), expected.at(i));
626 }
627}
628
629void tst_QDoubleSpinBox::setMinMax_data()
630{
631 QTest::addColumn<double>(name: "startValue");
632 QTest::addColumn<double>(name: "minimum");
633 QTest::addColumn<double>(name: "maximum");
634 QTest::addColumn<QTestEventList>(name: "keys");
635 QTest::addColumn<double>(name: "expected");
636 QTest::addColumn<bool>(name: "show");
637
638 QTestEventList keys;
639 keys.addKeyClick(qtKey: Qt::Key_Up);
640 keys.addKeyClick(qtKey: Qt::Key_Up);
641 keys.addKeyClick(qtKey: Qt::Key_Up);
642 keys.addKeyClick(qtKey: Qt::Key_Up);
643 keys.addKeyClick(qtKey: Qt::Key_Up);
644 QTest::newRow(dataTag: "data0") << 1.0 << -DBL_MAX << 2.0 << keys << 2.0 << false;
645 QTest::newRow(dataTag: "data1") << 1.0 << -DBL_MAX << 2.0 << keys << 2.0 << true;
646 QTest::newRow(dataTag: "data2") << -20000.0 << -15000.0 << -13000.0 << keys << -14995.0 << false;
647 QTest::newRow(dataTag: "data3") << -20000.0 << -15000.0 << -13000.0 << keys << -14995.0 << true;
648 QTest::newRow(dataTag: "data4") << 20.0 << -101.2 << -102.0 << QTestEventList() << -102.0 << false;
649 QTest::newRow(dataTag: "data5") << 20.0 << -101.2 << -102.0 << QTestEventList() << -102.0 << true;
650}
651
652void tst_QDoubleSpinBox::setMinMax()
653{
654 QFETCH(double, startValue);
655 QFETCH(double, minimum);
656 QFETCH(double, maximum);
657 QFETCH(QTestEventList, keys);
658 QFETCH(double, expected);
659 QFETCH(bool, show);
660
661 {
662 QDoubleSpinBox spin(0);
663 spin.setMinimum(minimum);
664 spin.setMaximum(maximum);
665 spin.setValue(startValue);
666
667 if (show)
668 spin.show();
669 keys.simulate(w: &spin);
670 QCOMPARE(spin.value(), expected);
671 }
672
673 {
674 QDoubleSpinBox spin(0);
675 spin.setMaximum(maximum);
676 spin.setMinimum(minimum);
677 spin.setValue(startValue);
678
679 if (show)
680 spin.show();
681 keys.simulate(w: &spin);
682 }
683
684 {
685 QDoubleSpinBox spin(0);
686 spin.setRange(min: minimum, max: maximum);
687 spin.setValue(startValue);
688
689 if (show)
690 spin.show();
691 keys.simulate(w: &spin);
692 }
693
694
695}
696
697void tst_QDoubleSpinBox::setDecimals_data()
698{
699 QTest::addColumn<int>(name: "decimals");
700 QTest::addColumn<int>(name: "expectedDecimals");
701 QTest::addColumn<double>(name: "startValue");
702 QTest::addColumn<QString>(name: "expected");
703
704 QTest::newRow(dataTag: "data0") << 4 << 4 << 1.0 << "1.0000";
705 QTest::newRow(dataTag: "data1") << 1 << 1 << 1.243443 << "1.2";
706 QTest::newRow(dataTag: "data2") << 6 << 6 << 1.29 << "1.290000";
707 QTest::newRow(dataTag: "data3") << 8 << 8 << 9.1234567809 << "9.12345678";
708 QTest::newRow(dataTag: "data4") << 13 << 13 << 0.12345678901237 << "0.1234567890124";
709 QTest::newRow(dataTag: "data5") << 13 << 13 << -0.12345678901237 << "-0.1234567890124";
710 QTest::newRow(dataTag: "data6") << 13 << 13 << -0.12345678901237 << "-0.1234567890124";
711 QTest::newRow(dataTag: "data7") << -1 << 0 << 0.1 << "0";
712 QTest::newRow(dataTag: "data8") << 120 << 120 << -0.12345678901237 << "-0.123456789012370005131913330842508003115653991699218750000000000000000000000000000000000000000000000000000000000000000000";
713
714}
715
716void tst_QDoubleSpinBox::setDecimals()
717{
718 QFETCH(int, decimals);
719 QFETCH(int, expectedDecimals);
720 QFETCH(double, startValue);
721 QFETCH(QString, expected);
722
723 QDoubleSpinBox spin(0);
724 spin.setRange(min: -DBL_MAX, DBL_MAX);
725 spin.setDecimals(decimals);
726 spin.setValue(startValue);
727 QCOMPARE(spin.decimals(), expectedDecimals);
728 if (sizeof(qreal) == sizeof(float))
729 QCOMPARE(spin.text().left(17), expected.left(17));
730 else
731 QCOMPARE(spin.text(), expected);
732
733 if (spin.decimals() > 0) {
734#ifdef Q_OS_MAC
735 QTest::keyClick(&spin, Qt::Key_Right, Qt::ControlModifier);
736#else
737 QTest::keyClick(widget: &spin, key: Qt::Key_End);
738#endif
739 QTest::keyClick(widget: &spin, key: Qt::Key_1); // just make sure we can't input more decimals than what is specified
740 QTest::keyClick(widget: &spin, key: Qt::Key_1);
741 QTest::keyClick(widget: &spin, key: Qt::Key_1);
742 QTest::keyClick(widget: &spin, key: Qt::Key_1);
743 QTest::keyClick(widget: &spin, key: Qt::Key_1);
744 if (sizeof(qreal) == sizeof(float))
745 QCOMPARE(spin.text().left(17), expected.left(17));
746 else
747 QCOMPARE(spin.text(), expected);
748 }
749}
750
751static QString stateName(int state)
752{
753 switch (state) {
754 case QValidator::Acceptable: return QString("Acceptable");
755 case QValidator::Intermediate: return QString("Intermediate");
756 case QValidator::Invalid: return QString("Invalid");
757 default: break;
758 }
759 qWarning(msg: "%s %d: this should never happen", __FILE__, __LINE__);
760 return QString();
761}
762
763void tst_QDoubleSpinBox::valueFromTextAndValidate_data()
764{
765 const int Intermediate = QValidator::Intermediate;
766 const int Invalid = QValidator::Invalid;
767 const int Acceptable = QValidator::Acceptable;
768
769 QTest::addColumn<QString>(name: "txt");
770 QTest::addColumn<int>(name: "state");
771 QTest::addColumn<double>(name: "mini");
772 QTest::addColumn<double>(name: "maxi");
773 QTest::addColumn<int>(name: "language");
774 QTest::addColumn<QString>(name: "expectedText"); // if empty we don't check
775
776 QTest::newRow(dataTag: "data0") << QString("2.2") << Intermediate << 3.0 << 5.0 << (int)QLocale::C << QString();
777 QTest::newRow(dataTag: "data1") << QString() << Intermediate << 0.0 << 100.0 << (int)QLocale::C << QString();
778 QTest::newRow(dataTag: "data2") << QString("asd") << Invalid << 0.0 << 100.0 << (int)QLocale::C << QString();
779 QTest::newRow(dataTag: "data3") << QString("2.2") << Acceptable << 0.0 << 100.0 << (int)QLocale::C << QString();
780 QTest::newRow(dataTag: "data4") << QString(" ") << Intermediate << 0.0 << 100.0 << (int)QLocale::NorwegianBokmal << QString();
781 QTest::newRow(dataTag: "data5") << QString(" ") << Intermediate << 0.0 << 100.0 << (int)QLocale::C << QString();
782 QTest::newRow(dataTag: "data6") << QString(",") << Intermediate << 0.0 << 100.0 << (int)QLocale::NorwegianBokmal << QString();
783 QTest::newRow(dataTag: "data7") << QString(",") << Invalid << 0.0 << 100.0 << (int)QLocale::C << QString();
784 QTest::newRow(dataTag: "data8") << QString("1 ") << Acceptable << 0.0 << 1000.0 << (int)QLocale::NorwegianBokmal << QString("1");
785 QTest::newRow(dataTag: "data9") << QString("1 ") << Acceptable << 0.0 << 100.0 << (int)QLocale::C << QString("1");
786 QTest::newRow(dataTag: "data10") << QString(" 1") << Acceptable << 0.0 << 100.0 << (int)QLocale::NorwegianBokmal << QString("1");
787 QTest::newRow(dataTag: "data11") << QString(" 1") << Acceptable << 0.0 << 100.0 << (int)QLocale::C << QString("1");
788 QTest::newRow(dataTag: "data12") << QString("1,") << Acceptable << 0.0 << 100.0 << (int)QLocale::NorwegianBokmal << QString();
789 QTest::newRow(dataTag: "data13") << QString("1,") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString();
790 QTest::newRow(dataTag: "data14") << QString("1, ") << Acceptable << 0.0 << 100.0 << (int)QLocale::NorwegianBokmal << QString("1,");
791 QTest::newRow(dataTag: "data15") << QString("1, ") << Invalid << 0.0 << 100.0 << (int)QLocale::C << QString();
792 QTest::newRow(dataTag: "data16") << QString("2") << Intermediate << 100.0 << 102.0 << (int)QLocale::C << QString();
793 QTest::newRow(dataTag: "data17") << QString("22.0") << Intermediate << 100.0 << 102.0 << (int)QLocale::C << QString();
794 QTest::newRow(dataTag: "data18") << QString("12.0") << Intermediate << 100.0 << 102.0 << (int)QLocale::C << QString();
795 QTest::newRow(dataTag: "data19") << QString("12.2") << Intermediate << 100. << 102.0 << (int)QLocale::C << QString();
796 QTest::newRow(dataTag: "data20") << QString("21.") << Intermediate << 100.0 << 102.0 << (int)QLocale::C << QString();
797 QTest::newRow(dataTag: "data21") << QString("-21.") << Intermediate << -102.0 << -100.0 << (int)QLocale::C << QString();
798 QTest::newRow(dataTag: "data22") << QString("-12.") << Intermediate << -102.0 << -100.0 << (int)QLocale::C << QString();
799 QTest::newRow(dataTag: "data23") << QString("-11.11") << Intermediate << -102.0 << -101.2 << (int)QLocale::C << QString();
800 QTest::newRow(dataTag: "data24") << QString("-11.4") << Intermediate << -102.0 << -101.3 << (int)QLocale::C << QString();
801 QTest::newRow(dataTag: "data25") << QString("11.400") << Invalid << 0.0 << 100.0 << (int)QLocale::C << QString();
802 QTest::newRow(dataTag: "data26") << QString(".4") << Intermediate << 0.45 << 0.5 << (int)QLocale::C << QString();
803 QTest::newRow(dataTag: "data27") << QString("+.4") << Intermediate << 0.45 << 0.5 << (int)QLocale::C << QString();
804 QTest::newRow(dataTag: "data28") << QString("-.4") << Intermediate << -0.5 << -0.45 << (int)QLocale::C << QString();
805 QTest::newRow(dataTag: "data29") << QString(".4") << Intermediate << 1.0 << 2.4 << (int)QLocale::C << QString();
806 QTest::newRow(dataTag: "data30") << QString("-.4") << Intermediate << -2.3 << -1.9 << (int)QLocale::C << QString();
807 QTest::newRow(dataTag: "data31") << QString("-42") << Invalid << -2.43 << -1.0 << (int)QLocale::C << QString();
808 QTest::newRow(dataTag: "data32") << QString("-4") << Invalid << -1.4 << -1.0 << (int)QLocale::C << QString();
809 QTest::newRow(dataTag: "data33") << QString("-42") << Invalid << -1.4 << -1.0 << (int)QLocale::C << QString();
810 QTest::newRow(dataTag: "data34") << QString("1000000000000") << Invalid << -140.0 << -120.2 << (int)QLocale::C << QString();
811 QTest::newRow(dataTag: "data35") << QString("+.12") << Invalid << -5.0 << -3.2 << (int)QLocale::C << QString();
812 QTest::newRow(dataTag: "data36") << QString("-.12") << Invalid << 5.0 << 33.2 << (int)QLocale::C << QString();
813 QTest::newRow(dataTag: "data37") << QString("12.2") << Intermediate << 100. << 103.0 << (int)QLocale::C << QString();
814 QTest::newRow(dataTag: "data38") << QString("12.2") << Intermediate << 100. << 102.3 << (int)QLocale::C << QString();
815 QTest::newRow(dataTag: "data39") << QString("-12.") << Acceptable << -102.0 << 102.0 << (int)QLocale::C << QString();
816 QTest::newRow(dataTag: "data40") << QString("12.") << Invalid << -102.0 << 11.0 << (int)QLocale::C << QString();
817 QTest::newRow(dataTag: "data41") << QString("103.") << Invalid << -102.0 << 11.0 << (int)QLocale::C << QString();
818 QTest::newRow(dataTag: "data42") << QString("122") << Invalid << 10.0 << 12.2 << (int)QLocale::C << QString();
819 QTest::newRow(dataTag: "data43") << QString("-2.2") << Intermediate << -12.2 << -3.2 << (int)QLocale::C << QString();
820 QTest::newRow(dataTag: "data44") << QString("-2.20") << Intermediate << -12.1 << -3.2 << (int)QLocale::C << QString();
821 QTest::newRow(dataTag: "data45") << QString("200,2") << Invalid << 0.0 << 1000.0 << (int)QLocale::C << QString();
822 QTest::newRow(dataTag: "data46") << QString("200,2") << Acceptable << 0.0 << 1000.0 << (int)QLocale::German << QString();
823 QTest::newRow(dataTag: "data47") << QString("2.2") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString();
824 QTest::newRow(dataTag: "data48") << QString("2.2") << Acceptable << 0.0 << 1000.0 << (int)QLocale::German << QString();
825 QTest::newRow(dataTag: "data49") << QString("2.2,00") << Acceptable << 0.0 << 1000.0 << (int)QLocale::German << QString();
826 QTest::newRow(dataTag: "data50") << QString("2.2") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString();
827 QTest::newRow(dataTag: "data51") << QString("2.2,00") << Invalid << 0.0 << 1000.0 << (int)QLocale::C << QString();
828 QTest::newRow(dataTag: "data52") << QString("2..2,00") << Invalid << 0.0 << 1000.0 << (int)QLocale::German << QString();
829 QTest::newRow(dataTag: "data53") << QString("2.2") << Invalid << 0.0 << 1000.0 << (int)QLocale::NorwegianBokmal << QString();
830 QTest::newRow(dataTag: "data54") << QString(" 2.2") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString();
831 QTest::newRow(dataTag: "data55") << QString("2.2 ") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString("2.2");
832 QTest::newRow(dataTag: "data56") << QString(" 2.2 ") << Acceptable << 0.0 << 1000.0 << (int)QLocale::C << QString("2.2");
833 QTest::newRow(dataTag: "data57") << QString("2 2") << Invalid << 0.0 << 1000.0 << (int)QLocale::C << QString();
834}
835
836void tst_QDoubleSpinBox::valueFromTextAndValidate()
837{
838 QFETCH(QString, txt);
839 QFETCH(int, state);
840 QFETCH(double, mini);
841 QFETCH(double, maxi);
842 QFETCH(int, language);
843 QFETCH(QString, expectedText);
844 QLocale::setDefault(QLocale((QLocale::Language)language));
845
846 DoubleSpinBox sb(0);
847 sb.show();
848 sb.setRange(min: mini, max: maxi);
849
850 int unused = 0;
851 QCOMPARE(stateName(sb.validate(txt, unused)), stateName(state));
852 if (!expectedText.isEmpty())
853 QCOMPARE(txt, expectedText);
854}
855
856void tst_QDoubleSpinBox::setReadOnly()
857{
858 QDoubleSpinBox spin(0);
859 spin.setValue(0.2);
860 spin.show();
861 QVERIFY(QTest::qWaitForWindowActive(&spin));
862 QCOMPARE(spin.value(), 0.2);
863 QTest::keyClick(widget: &spin, key: Qt::Key_Up);
864 QCOMPARE(spin.value(), 1.2);
865 spin.setReadOnly(true);
866 QTest::keyClick(widget: &spin, key: Qt::Key_Up);
867 QCOMPARE(spin.value(), 1.2);
868 spin.stepBy(steps: 1);
869 QCOMPARE(spin.value(), 2.2);
870 spin.setReadOnly(false);
871 QTest::keyClick(widget: &spin, key: Qt::Key_Up);
872 QCOMPARE(spin.value(), 3.2);
873}
874
875void tst_QDoubleSpinBox::editingFinished()
876{
877 QVBoxLayout *layout = new QVBoxLayout(testFocusWidget);
878 QDoubleSpinBox *box = new QDoubleSpinBox(testFocusWidget);
879 layout->addWidget(box);
880 QDoubleSpinBox *box2 = new QDoubleSpinBox(testFocusWidget);
881 layout->addWidget(box2);
882
883 testFocusWidget->show();
884 testFocusWidget->activateWindow();
885 QVERIFY(QTest::qWaitForWindowActive(testFocusWidget));
886 box->setFocus();
887 QTRY_VERIFY(box->hasFocus());
888
889 QSignalSpy editingFinishedSpy1(box, SIGNAL(editingFinished()));
890 QSignalSpy editingFinishedSpy2(box2, SIGNAL(editingFinished()));
891
892 QTest::keyClick(widget: box, key: Qt::Key_Up);
893 QTest::keyClick(widget: box, key: Qt::Key_Up);
894
895
896 QCOMPARE(editingFinishedSpy1.count(), 0);
897 QCOMPARE(editingFinishedSpy2.count(), 0);
898
899 QTest::keyClick(widget: box2, key: Qt::Key_Up);
900 QTest::keyClick(widget: box2, key: Qt::Key_Up);
901 box2->setFocus();
902 QCOMPARE(editingFinishedSpy1.count(), 1);
903 box->setFocus();
904 QCOMPARE(editingFinishedSpy1.count(), 1);
905 QCOMPARE(editingFinishedSpy2.count(), 1);
906 QTest::keyClick(widget: box, key: Qt::Key_Up);
907 QCOMPARE(editingFinishedSpy1.count(), 1);
908 QCOMPARE(editingFinishedSpy2.count(), 1);
909 QTest::keyClick(widget: box, key: Qt::Key_Enter);
910 QCOMPARE(editingFinishedSpy1.count(), 2);
911 QCOMPARE(editingFinishedSpy2.count(), 1);
912 QTest::keyClick(widget: box, key: Qt::Key_Return);
913 QCOMPARE(editingFinishedSpy1.count(), 3);
914 QCOMPARE(editingFinishedSpy2.count(), 1);
915 box2->setFocus();
916 QCOMPARE(editingFinishedSpy1.count(), 4);
917 QCOMPARE(editingFinishedSpy2.count(), 1);
918 QTest::keyClick(widget: box2, key: Qt::Key_Enter);
919 QCOMPARE(editingFinishedSpy1.count(), 4);
920 QCOMPARE(editingFinishedSpy2.count(), 2);
921 QTest::keyClick(widget: box2, key: Qt::Key_Return);
922 QCOMPARE(editingFinishedSpy1.count(), 4);
923 QCOMPARE(editingFinishedSpy2.count(), 3);
924 testFocusWidget->hide();
925 QCOMPARE(editingFinishedSpy1.count(), 4);
926 QCOMPARE(editingFinishedSpy2.count(), 4);
927
928 // On some platforms this is our root window
929 // we need to show it again otherwise subsequent
930 // tests will fail
931 testFocusWidget->show();
932}
933
934void tst_QDoubleSpinBox::removeAll()
935{
936 DoubleSpinBox spin(0);
937 spin.setPrefix("foo");
938 spin.setSuffix("bar");
939 spin.setValue(0.2);
940 spin.setDecimals(1);
941 spin.show();
942#ifdef Q_OS_MAC
943 QTest::keyClick(&spin, Qt::Key_Left, Qt::ControlModifier);
944#else
945 QTest::keyClick(widget: &spin, key: Qt::Key_Home);
946#endif
947
948#ifdef Q_OS_MAC
949 QTest::keyClick(&spin, Qt::Key_Right, Qt::ControlModifier|Qt::ShiftModifier);
950#else
951 QTest::keyClick(widget: &spin, key: Qt::Key_End, modifier: Qt::ShiftModifier);
952#endif
953
954 QCOMPARE(spin.lineEdit()->selectedText(), QString("foo0.2bar"));
955 QTest::keyClick(widget: &spin, key: Qt::Key_1);
956 QCOMPARE(spin.text(), QString("foo1bar"));
957}
958
959void tst_QDoubleSpinBox::task54433()
960{
961 DoubleSpinBox priceSpinBox;
962 QCOMPARE(priceSpinBox.decimals(), 2);
963 priceSpinBox.show();
964 priceSpinBox.setRange(min: 0.0, max: 999.99);
965 priceSpinBox.setDecimals(2);
966 priceSpinBox.setValue(999.99);
967 QCOMPARE(priceSpinBox.text(), QString("999.99"));
968 QCOMPARE(priceSpinBox.value(), 999.99);
969 QCOMPARE(priceSpinBox.maximum(), 999.99);
970 priceSpinBox.setDecimals(1);
971 QCOMPARE(priceSpinBox.value(), 1000.0);
972 QCOMPARE(priceSpinBox.maximum(), 1000.0);
973 QCOMPARE(priceSpinBox.text(), QString("1000.0"));
974
975 priceSpinBox.setDecimals(2);
976 priceSpinBox.setRange(min: -999.99, max: 0.0);
977 priceSpinBox.setValue(-999.99);
978 QCOMPARE(priceSpinBox.text(), QString("-999.99"));
979 QCOMPARE(priceSpinBox.value(), -999.99);
980 QCOMPARE(priceSpinBox.minimum(), -999.99);
981 priceSpinBox.setDecimals(1);
982 QCOMPARE(priceSpinBox.value(), -1000.0);
983 QCOMPARE(priceSpinBox.minimum(), -1000.0);
984 QCOMPARE(priceSpinBox.text(), QString("-1000.0"));
985}
986
987
988
989void tst_QDoubleSpinBox::germanTest()
990{
991 QLocale::setDefault(QLocale(QLocale::German));
992 DoubleSpinBox spin;
993 spin.show();
994 spin.setValue(2.12);
995#ifdef Q_OS_MAC
996 QTest::keyClick(&spin, Qt::Key_Right, Qt::ControlModifier);
997#else
998 QTest::keyClick(widget: &spin, key: Qt::Key_End);
999#endif
1000 QTest::keyClick(widget: &spin, key: Qt::Key_Backspace);
1001 QCOMPARE(spin.text(), QString("2,1"));
1002 QTest::keyClick(widget: &spin, key: Qt::Key_Enter);
1003 QCOMPARE(spin.text(), QString("2,10"));
1004}
1005
1006void tst_QDoubleSpinBox::doubleDot()
1007{
1008 DoubleSpinBox spin;
1009 spin.show();
1010 spin.setValue(2.12);
1011 QTest::keyClick(widget: &spin, key: Qt::Key_Backspace);
1012 QCOMPARE(spin.text(), QString("2.12"));
1013#ifdef Q_OS_MAC
1014 QTest::keyClick(&spin, Qt::Key_Left, Qt::ControlModifier);
1015#else
1016 QTest::keyClick(widget: &spin, key: Qt::Key_Home);
1017#endif
1018 QTest::keyClick(widget: &spin, key: Qt::Key_Right, modifier: Qt::ShiftModifier);
1019 QCOMPARE(spin.lineEdit()->selectedText(), QString("2"));
1020 QTest::keyClick(widget: &spin, key: Qt::Key_1);
1021 QCOMPARE(spin.text(), QString("1.12"));
1022 QCOMPARE(spin.lineEdit()->cursorPosition(), 1);
1023 QTest::keyClick(widget: &spin, key: Qt::Key_Period);
1024 QCOMPARE(spin.text(), QString("1.12"));
1025 QCOMPARE(spin.lineEdit()->cursorPosition(), 2);
1026}
1027
1028void tst_QDoubleSpinBox::undoRedo()
1029{
1030 //test undo/redo feature (in conjunction with the "undoRedoEnabled" property)
1031 DoubleSpinBox spin(0);
1032 spin.show();
1033
1034 //the undo/redo is disabled by default
1035
1036 QCOMPARE(spin.value(), 0.0); //this is the default value
1037 QVERIFY(!spin.lineEdit()->isUndoAvailable());
1038 QVERIFY(!spin.lineEdit()->isRedoAvailable());
1039
1040 spin.lineEdit()->selectAll(); //ensures everything is selected and will be cleared by typing a key
1041 QTest::keyClick(widget: &spin, key: Qt::Key_1); //we put 1 into the spinbox
1042 QCOMPARE(spin.value(), 1.0);
1043 QVERIFY(spin.lineEdit()->isUndoAvailable());
1044
1045 //testing CTRL+Z (undo)
1046 int val = QKeySequence(QKeySequence::Undo)[0];
1047 if (val != 0) {
1048 Qt::KeyboardModifiers mods = (Qt::KeyboardModifiers)(val & Qt::KeyboardModifierMask);
1049 QTest::keyClick(widget: &spin, key: val & ~mods, modifier: mods);
1050 QCOMPARE(spin.value(), 0.0);
1051 QVERIFY(!spin.lineEdit()->isUndoAvailable());
1052 QVERIFY(spin.lineEdit()->isRedoAvailable());
1053 } else {
1054 QWARN("Undo not tested because no key sequence associated to QKeySequence::Redo");
1055 }
1056
1057
1058 //testing CTRL+Y (redo)
1059 val = QKeySequence(QKeySequence::Redo)[0];
1060 if (val != 0) {
1061 Qt::KeyboardModifiers mods = (Qt::KeyboardModifiers)(val & Qt::KeyboardModifierMask);
1062 QTest::keyClick(widget: &spin, key: val & ~mods, modifier: mods);
1063 QCOMPARE(spin.value(), 1.0);
1064 QVERIFY(!spin.lineEdit()->isRedoAvailable());
1065 QVERIFY(spin.lineEdit()->isUndoAvailable());
1066 } else {
1067 QWARN("Redo not tested because no key sequence associated to QKeySequence::Redo");
1068 }
1069
1070
1071 spin.setValue(55.0);
1072 QVERIFY(!spin.lineEdit()->isUndoAvailable());
1073 QVERIFY(!spin.lineEdit()->isRedoAvailable());
1074}
1075
1076struct task199226_DoubleSpinBox : public QDoubleSpinBox
1077{
1078 task199226_DoubleSpinBox(QWidget *parent = 0) : QDoubleSpinBox(parent) {}
1079 QLineEdit *lineEdit() { return QAbstractSpinBox::lineEdit(); }
1080};
1081
1082void tst_QDoubleSpinBox::task199226_stateAfterEnter()
1083{
1084 task199226_DoubleSpinBox spin;
1085 spin.setMinimum(0);
1086 spin.setMaximum(10);
1087 spin.setDecimals(0);
1088 spin.show();
1089 QTest::mouseDClick(widget: spin.lineEdit(), button: Qt::LeftButton);
1090 QTest::keyClick(widget: spin.lineEdit(), key: Qt::Key_3);
1091 QVERIFY(spin.lineEdit()->isModified());
1092 QVERIFY(spin.lineEdit()->isUndoAvailable());
1093 QTest::keyClick(widget: spin.lineEdit(), key: Qt::Key_Enter);
1094 QVERIFY(!spin.lineEdit()->isModified());
1095 QVERIFY(!spin.lineEdit()->isUndoAvailable());
1096}
1097
1098class task224497_fltMax_DoubleSpinBox : public QDoubleSpinBox
1099{
1100public:
1101 QLineEdit * lineEdit () const { return QDoubleSpinBox::lineEdit(); }
1102};
1103
1104void tst_QDoubleSpinBox::task224497_fltMax()
1105{
1106 task224497_fltMax_DoubleSpinBox *dspin = new task224497_fltMax_DoubleSpinBox;
1107 dspin->setMinimum(3);
1108 dspin->setMaximum(FLT_MAX);
1109 dspin->show();
1110 QVERIFY(QTest::qWaitForWindowActive(dspin));
1111 dspin->lineEdit()->selectAll();
1112 QTest::keyClick(widget: dspin->lineEdit(), key: Qt::Key_Delete);
1113 QTest::keyClick(widget: dspin->lineEdit(), key: Qt::Key_1);
1114 QCOMPARE(dspin->cleanText(), QLatin1String("1"));
1115}
1116
1117void tst_QDoubleSpinBox::task221221()
1118{
1119 QDoubleSpinBox spin;
1120 QTest::keyClick(widget: &spin, key: Qt::Key_1);
1121 spin.show();
1122 QVERIFY(QTest::qWaitForWindowExposed(&spin));
1123 QCOMPARE(spin.text(), QLatin1String("1"));
1124}
1125
1126void tst_QDoubleSpinBox::task255471_decimalsValidation()
1127{
1128 // QDoubleSpinBox shouldn't crash with large numbers of decimals. Even if
1129 // the results are useless ;-)
1130 for (int i = 0; i < 32; ++i)
1131 {
1132 QDoubleSpinBox spinBox;
1133 spinBox.setDecimals(i);
1134 spinBox.setMinimum(0.3);
1135 spinBox.setMaximum(12);
1136
1137 spinBox.show();
1138 QVERIFY(QTest::qWaitForWindowExposed(&spinBox));
1139 spinBox.setFocus();
1140 QTRY_VERIFY(spinBox.hasFocus());
1141
1142 QTest::keyPress(widget: &spinBox, key: Qt::Key_Right);
1143 QTest::keyPress(widget: &spinBox, key: Qt::Key_Right);
1144 QTest::keyPress(widget: &spinBox, key: Qt::Key_Delete);
1145
1146 // Don't crash!
1147 QTest::keyPress(widget: &spinBox, key: Qt::Key_2);
1148 }
1149}
1150
1151void tst_QDoubleSpinBox::taskQTBUG_5008_textFromValueAndValidate()
1152{
1153 class DecoratedSpinBox : public QDoubleSpinBox
1154 {
1155 public:
1156 DecoratedSpinBox()
1157 {
1158 setLocale(QLocale::French);
1159 setMaximum(100000000);
1160 setValue(1000);
1161 }
1162
1163 QLineEdit *lineEdit() const
1164 {
1165 return QDoubleSpinBox::lineEdit();
1166 }
1167
1168 //we use the French delimiters here
1169 QString textFromValue (double value) const
1170 {
1171 return locale().toString(i: value);
1172 }
1173 } spinbox;
1174 spinbox.show();
1175 spinbox.activateWindow();
1176 spinbox.setFocus();
1177 QApplication::setActiveWindow(&spinbox);
1178 QVERIFY(QTest::qWaitForWindowActive(&spinbox));
1179 QCOMPARE(static_cast<QWidget *>(&spinbox), QApplication::activeWindow());
1180 QTRY_VERIFY(spinbox.hasFocus());
1181 QCOMPARE(spinbox.text(), spinbox.locale().toString(spinbox.value()));
1182 spinbox.lineEdit()->setCursorPosition(2); //just after the first thousand separator
1183 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_0); // let's insert a 0
1184 QCOMPARE(spinbox.value(), 10000.);
1185 spinbox.clearFocus(); //make sure the value is correctly formatted
1186 QCOMPARE(spinbox.text(), spinbox.locale().toString(spinbox.value()));
1187}
1188
1189void tst_QDoubleSpinBox::taskQTBUG_6670_selectAllWithPrefix()
1190{
1191 DoubleSpinBox spin;
1192 spin.setPrefix("$ ");
1193 spin.lineEdit()->selectAll();
1194 QTest::keyClick(widget: spin.lineEdit(), key: Qt::Key_1);
1195 QCOMPARE(spin.value(), 1.);
1196 QTest::keyClick(widget: spin.lineEdit(), key: Qt::Key_2);
1197 QCOMPARE(spin.value(), 12.);
1198}
1199
1200void tst_QDoubleSpinBox::taskQTBUG_6496_fiddlingWithPrecision()
1201{
1202 QDoubleSpinBox dsb;
1203 dsb.setRange(min: 0, max: 0.991);
1204 dsb.setDecimals(1);
1205 QCOMPARE(dsb.maximum(), 1.0);
1206 dsb.setDecimals(2);
1207 QCOMPARE(dsb.maximum(), 0.99);
1208 dsb.setDecimals(3);
1209 QCOMPARE(dsb.maximum(), 0.991);
1210}
1211
1212void tst_QDoubleSpinBox::setGroupSeparatorShown_data()
1213{
1214 QTest::addColumn<QLocale::Language>(name: "lang");
1215 QTest::addColumn<QLocale::Country>(name: "country");
1216
1217 QTest::newRow(dataTag: "data0") << QLocale::English << QLocale::UnitedStates;
1218 QTest::newRow(dataTag: "data1") << QLocale::Swedish << QLocale::Sweden;
1219 QTest::newRow(dataTag: "data2") << QLocale::German << QLocale::Germany;
1220 QTest::newRow(dataTag: "data3") << QLocale::Georgian << QLocale::Georgia;
1221 QTest::newRow(dataTag: "data3") << QLocale::Macedonian << QLocale::Macedonia;
1222}
1223
1224void tst_QDoubleSpinBox::setGroupSeparatorShown()
1225{
1226 QFETCH(QLocale::Language, lang);
1227 QFETCH(QLocale::Country, country);
1228
1229 QLocale loc(lang, country);
1230 QLocale::setDefault(loc);
1231 DoubleSpinBox spinBox;
1232 spinBox.setMaximum(99999999);
1233 spinBox.setValue(1300000.00);
1234 spinBox.setGroupSeparatorShown(true);
1235 QCOMPARE(spinBox.lineEdit()->text(), spinBox.locale().toString(1300000.00, 'f', 2));
1236 QCOMPARE(spinBox.isGroupSeparatorShown(), true);
1237 QCOMPARE(spinBox.textFromValue(23421),spinBox.locale().toString(23421.00, 'f', 2));
1238
1239 spinBox.setGroupSeparatorShown(false);
1240 QCOMPARE(spinBox.lineEdit()->text(), spinBox.locale().toString(1300000.00, 'f', 2).remove(
1241 spinBox.locale().groupSeparator()));
1242 QCOMPARE(spinBox.isGroupSeparatorShown(), false);
1243
1244 spinBox.setMaximum(72000);
1245 spinBox.lineEdit()->setText(spinBox.locale().toString(i: 32000.64, f: 'f', prec: 2));
1246 QCOMPARE(spinBox.value()+1000, 33000.64);
1247
1248 spinBox.lineEdit()->setText(spinBox.locale().toString(i: 32000.44, f: 'f', prec: 2));
1249 QCOMPARE(spinBox.value()+1000, 33000.44);
1250}
1251
1252void tst_QDoubleSpinBox::adaptiveDecimalStep()
1253{
1254 DoubleSpinBox spinBox;
1255 spinBox.setRange(min: -100000, max: 100000);
1256 spinBox.setDecimals(4);
1257 spinBox.setStepType(DoubleSpinBox::StepType::AdaptiveDecimalStepType);
1258
1259 // Positive values
1260
1261 spinBox.setValue(0);
1262
1263 // Go from 0 to 0.01
1264 for (double i = 0; i < 0.00999; i += 0.0001) {
1265 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1266 spinBox.stepBy(steps: 1);
1267 }
1268
1269 // Go from 0.01 to 0.1
1270 for (double i = 0.01; i < 0.0999; i += 0.001) {
1271 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1272 spinBox.stepBy(steps: 1);
1273 }
1274
1275 // Go from 0.1 to 1
1276 for (double i = 0.1; i < 0.999; i += 0.01) {
1277 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1278 spinBox.stepBy(steps: 1);
1279 }
1280
1281 // Go from 1 to 10
1282 for (double i = 1; i < 9.99; i += 0.1) {
1283 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1284 spinBox.stepBy(steps: 1);
1285 }
1286
1287 // Go from 10 to 100
1288 for (int i = 10; i < 100; i++) {
1289 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1290 spinBox.stepBy(steps: 1);
1291 }
1292
1293 // Go from 100 to 1000
1294 for (int i = 100; i < 1000; i += 10) {
1295 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1296 spinBox.stepBy(steps: 1);
1297 }
1298
1299 // Go from 1000 to 10000
1300 for (int i = 1000; i < 10000; i += 100) {
1301 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1302 spinBox.stepBy(steps: 1);
1303 }
1304
1305 // Test decreasing the values now
1306
1307 // Go from 10000 down to 1000
1308 for (int i = 10000; i > 1000; i -= 100) {
1309 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1310 spinBox.stepBy(steps: -1);
1311 }
1312
1313 // Go from 1000 down to 100
1314 for (int i = 1000; i > 100; i -= 10) {
1315 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1316 spinBox.stepBy(steps: -1);
1317 }
1318
1319 // Negative values
1320
1321 spinBox.setValue(0);
1322
1323 // Go from 0 to -0.01
1324 for (double i = 0; i > -0.00999; i -= 0.0001) {
1325 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1326 spinBox.stepBy(steps: -1);
1327 }
1328
1329 // Go from -0.01 to -0.1
1330 for (double i = -0.01; i > -0.0999; i -= 0.001) {
1331 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1332 spinBox.stepBy(steps: -1);
1333 }
1334
1335 // Go from -0.1 to -1
1336 for (double i = -0.1; i > -0.999; i -= 0.01) {
1337 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1338 spinBox.stepBy(steps: -1);
1339 }
1340
1341 // Go from -1 to -10
1342 for (double i = -1; i > -9.99; i -= 0.1) {
1343 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1344 spinBox.stepBy(steps: -1);
1345 }
1346
1347 // Go from -10 to -100
1348 for (int i = -10; i > -100; i--) {
1349 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1350 spinBox.stepBy(steps: -1);
1351 }
1352
1353 // Go from -100 to -1000
1354 for (int i = -100; i > -1000; i -= 10) {
1355 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1356 spinBox.stepBy(steps: -1);
1357 }
1358
1359 // Go from 1000 to 10000
1360 for (int i = -1000; i > -10000; i -= 100) {
1361 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1362 spinBox.stepBy(steps: -1);
1363 }
1364
1365 // Test increasing the values now
1366
1367 // Go from -10000 up to -1000
1368 for (int i = -10000; i < -1000; i += 100) {
1369 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1370 spinBox.stepBy(steps: 1);
1371 }
1372
1373 // Go from -1000 up to -100
1374 for (int i = -1000; i < -100; i += 10) {
1375 QVERIFY(qFuzzyCompare(spinBox.value(), i));
1376 spinBox.stepBy(steps: 1);
1377 }
1378}
1379
1380void tst_QDoubleSpinBox::wheelEvents_data()
1381{
1382#if QT_CONFIG(wheelevent)
1383 QTest::addColumn<QPoint>(name: "angleDelta");
1384 QTest::addColumn<int>(name: "stepModifier");
1385 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifier");
1386 QTest::addColumn<Qt::MouseEventSource>(name: "source");
1387 QTest::addColumn<double>(name: "start");
1388 QTest::addColumn<DoubleList>(name: "expectedValues");
1389
1390 const auto fractions = {false, true};
1391
1392 const auto directions = {true, false};
1393
1394 const auto modifierList = {Qt::NoModifier,
1395 Qt::ShiftModifier,
1396 Qt::ControlModifier,
1397 Qt::AltModifier,
1398 Qt::MetaModifier};
1399
1400 const auto validStepModifierList = {Qt::NoModifier,
1401 Qt::ControlModifier,
1402 Qt::ShiftModifier};
1403
1404 const auto sources = {Qt::MouseEventNotSynthesized,
1405 Qt::MouseEventSynthesizedBySystem,
1406 Qt::MouseEventSynthesizedByQt,
1407 Qt::MouseEventSynthesizedByApplication};
1408
1409 const double startValue = 0.0;
1410
1411 for (auto fraction : fractions) {
1412 for (auto up : directions) {
1413
1414 const int units = (fraction ? 60 : 120) * (up ? 1 : -1);
1415
1416 for (auto modifier : modifierList) {
1417
1418 const Qt::KeyboardModifiers modifiers(modifier);
1419
1420 const auto modifierName = modifierToName(modifier);
1421 if (modifierName.isEmpty())
1422 continue;
1423
1424 for (auto stepModifier : validStepModifierList) {
1425
1426 const auto stepModifierName = modifierToName(modifier: stepModifier);
1427 if (stepModifierName.isEmpty())
1428 continue;
1429
1430 const int steps = (modifier & stepModifier ? 10 : 1)
1431 * (up ? 1 : -1);
1432
1433 for (auto source : sources) {
1434
1435#ifdef Q_OS_MACOS
1436 QPoint angleDelta;
1437 if ((modifier & Qt::ShiftModifier) &&
1438 source == Qt::MouseEventNotSynthesized) {
1439 // On macOS the Shift modifier converts vertical
1440 // mouse wheel events to horizontal.
1441 angleDelta = { units, 0 };
1442 } else {
1443 // However, this is not the case for trackpad scroll
1444 // events.
1445 angleDelta = { 0, units };
1446 }
1447#else
1448 const QPoint angleDelta(0, units);
1449#endif
1450
1451 QLatin1String sourceName;
1452 switch (source) {
1453 case Qt::MouseEventNotSynthesized:
1454 sourceName = QLatin1String("NotSynthesized");
1455 break;
1456 case Qt::MouseEventSynthesizedBySystem:
1457 sourceName = QLatin1String("SynthesizedBySystem");
1458 break;
1459 case Qt::MouseEventSynthesizedByQt:
1460 sourceName = QLatin1String("SynthesizedByQt");
1461 break;
1462 case Qt::MouseEventSynthesizedByApplication:
1463 sourceName = QLatin1String("SynthesizedByApplication");
1464 break;
1465 default:
1466 qFatal(msg: "Unexpected wheel event source");
1467 continue;
1468 }
1469
1470 DoubleList expectedValues;
1471 if (fraction)
1472 expectedValues << startValue;
1473 expectedValues << startValue + steps;
1474
1475 QTest::addRow(format: "%s%s%sWith%sKeyboardModifier%s",
1476 fraction ? "half" : "full",
1477 up ? "Up" : "Down",
1478 stepModifierName.latin1(),
1479 modifierName.latin1(),
1480 sourceName.latin1())
1481 << angleDelta
1482 << static_cast<int>(stepModifier)
1483 << modifiers
1484 << source
1485 << startValue
1486 << expectedValues;
1487 }
1488 }
1489 }
1490 }
1491 }
1492#else
1493 QSKIP("Built with --no-feature-wheelevent");
1494#endif
1495}
1496
1497void tst_QDoubleSpinBox::wheelEvents()
1498{
1499#if QT_CONFIG(wheelevent)
1500 QFETCH(QPoint, angleDelta);
1501 QFETCH(int, stepModifier);
1502 QFETCH(Qt::KeyboardModifiers, modifier);
1503 QFETCH(Qt::MouseEventSource, source);
1504 QFETCH(double, start);
1505 QFETCH(DoubleList, expectedValues);
1506
1507 DoubleSpinBox spinBox;
1508 spinBox.setRange(min: -20, max: 20);
1509 spinBox.setValue(start);
1510
1511 QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style(
1512 new StepModifierStyle);
1513 style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier);
1514 spinBox.setStyle(style.data());
1515
1516 QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta,
1517 Qt::NoButton, modifier, Qt::NoScrollPhase, false, source);
1518 for (int expected : expectedValues) {
1519 qApp->sendEvent(receiver: &spinBox, event: &event);
1520 QCOMPARE(spinBox.value(), expected);
1521 }
1522#else
1523 QSKIP("Built with --no-feature-wheelevent");
1524#endif
1525}
1526
1527void tst_QDoubleSpinBox::stepModifierKeys_data()
1528{
1529 QTest::addColumn<double>(name: "startValue");
1530 QTest::addColumn<int>(name: "stepModifier");
1531 QTest::addColumn<QTestEventList>(name: "keys");
1532 QTest::addColumn<double>(name: "expectedValue");
1533
1534 const auto keyList = {Qt::Key_Up, Qt::Key_Down};
1535
1536 const auto modifierList = {Qt::NoModifier,
1537 Qt::ShiftModifier,
1538 Qt::ControlModifier,
1539 Qt::AltModifier,
1540 Qt::MetaModifier};
1541
1542 const auto validStepModifierList = {Qt::NoModifier,
1543 Qt::ControlModifier,
1544 Qt::ShiftModifier};
1545
1546
1547 for (auto key : keyList) {
1548
1549 const bool up = key == Qt::Key_Up;
1550 Q_ASSERT(up || key == Qt::Key_Down);
1551
1552 const double startValue = up ? 0.0 : 10.0;
1553
1554 for (auto modifier : modifierList) {
1555
1556 QTestEventList keys;
1557 keys.addKeyClick(qtKey: key, modifiers: modifier);
1558
1559 const auto modifierName = modifierToName(modifier);
1560 if (modifierName.isEmpty())
1561 continue;
1562
1563 for (auto stepModifier : validStepModifierList) {
1564
1565 const auto stepModifierName = modifierToName(modifier: stepModifier);
1566 if (stepModifierName.isEmpty())
1567 continue;
1568
1569 const int steps = (modifier & stepModifier ? 10 : 1)
1570 * (up ? 1 : -1);
1571
1572 const double expectedValue = startValue + steps;
1573
1574 QTest::addRow(format: "%s%sWith%sKeyboardModifier",
1575 up ? "up" : "down",
1576 stepModifierName.latin1(),
1577 modifierName.latin1())
1578 << startValue
1579 << static_cast<int>(stepModifier)
1580 << keys
1581 << expectedValue;
1582 }
1583 }
1584 }
1585}
1586
1587void tst_QDoubleSpinBox::stepModifierKeys()
1588{
1589 QFETCH(double, startValue);
1590 QFETCH(int, stepModifier);
1591 QFETCH(QTestEventList, keys);
1592 QFETCH(double, expectedValue);
1593
1594 QDoubleSpinBox spin(0);
1595 spin.setValue(startValue);
1596
1597 QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style(
1598 new StepModifierStyle);
1599 style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier);
1600 spin.setStyle(style.data());
1601
1602 spin.show();
1603 QVERIFY(QTest::qWaitForWindowActive(&spin));
1604
1605 QCOMPARE(spin.value(), startValue);
1606 keys.simulate(w: &spin);
1607 QCOMPARE(spin.value(), expectedValue);
1608}
1609
1610void tst_QDoubleSpinBox::stepModifierButtons_data()
1611{
1612 QTest::addColumn<QStyle::SubControl>(name: "subControl");
1613 QTest::addColumn<int>(name: "stepModifier");
1614 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers");
1615 QTest::addColumn<double>(name: "startValue");
1616 QTest::addColumn<double>(name: "expectedValue");
1617
1618 const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
1619
1620 const auto modifierList = {Qt::NoModifier,
1621 Qt::ShiftModifier,
1622 Qt::ControlModifier,
1623 Qt::AltModifier,
1624 Qt::MetaModifier};
1625
1626 const auto validStepModifierList = {Qt::NoModifier,
1627 Qt::ControlModifier,
1628 Qt::ShiftModifier};
1629
1630 for (auto subControl : subControls) {
1631
1632 const bool up = subControl == QStyle::SC_SpinBoxUp;
1633 Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
1634
1635 const double startValue = up ? 0 : 10;
1636
1637 for (auto modifier : modifierList) {
1638
1639 const Qt::KeyboardModifiers modifiers(modifier);
1640
1641 const auto modifierName = modifierToName(modifier);
1642 if (modifierName.isEmpty())
1643 continue;
1644
1645 for (auto stepModifier : validStepModifierList) {
1646
1647 const auto stepModifierName = modifierToName(modifier: stepModifier);
1648 if (stepModifierName.isEmpty())
1649 continue;
1650
1651 const int steps = (modifier & stepModifier ? 10 : 1)
1652 * (up ? 1 : -1);
1653
1654 const double expectedValue = startValue + steps;
1655
1656 QTest::addRow(format: "%s%sWith%sKeyboardModifier",
1657 up ? "up" : "down",
1658 stepModifierName.latin1(),
1659 modifierName.latin1())
1660 << subControl
1661 << static_cast<int>(stepModifier)
1662 << modifiers
1663 << startValue
1664 << expectedValue;
1665 }
1666 }
1667 }
1668}
1669
1670void tst_QDoubleSpinBox::stepModifierButtons()
1671{
1672 QFETCH(QStyle::SubControl, subControl);
1673 QFETCH(int, stepModifier);
1674 QFETCH(Qt::KeyboardModifiers, modifiers);
1675 QFETCH(double, startValue);
1676 QFETCH(double, expectedValue);
1677
1678 DoubleSpinBox spin(0);
1679 spin.setRange(min: -20, max: 20);
1680 spin.setValue(startValue);
1681
1682 QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style(
1683 new StepModifierStyle);
1684 style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier);
1685 spin.setStyle(style.data());
1686
1687 spin.show();
1688 QVERIFY(QTest::qWaitForWindowActive(&spin));
1689
1690 QStyleOptionSpinBox spinBoxStyleOption;
1691 spin.initStyleOption(option: &spinBoxStyleOption);
1692
1693 const QRect buttonRect = spin.style()->subControlRect(
1694 cc: QStyle::CC_SpinBox, opt: &spinBoxStyleOption, sc: subControl, widget: &spin);
1695
1696 QCOMPARE(spin.value(), startValue);
1697 QTest::mouseClick(widget: &spin, button: Qt::LeftButton, stateKey: modifiers, pos: buttonRect.center());
1698 QCOMPARE(spin.value(), expectedValue);
1699}
1700
1701void tst_QDoubleSpinBox::stepModifierPressAndHold_data()
1702{
1703 QTest::addColumn<QStyle::SubControl>(name: "subControl");
1704 QTest::addColumn<int>(name: "stepModifier");
1705 QTest::addColumn<Qt::KeyboardModifiers>(name: "modifiers");
1706 QTest::addColumn<int>(name: "expectedStepModifier");
1707
1708 const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown};
1709
1710 const auto modifierList = {Qt::NoModifier,
1711 Qt::ShiftModifier,
1712 Qt::ControlModifier,
1713 Qt::AltModifier,
1714 Qt::MetaModifier};
1715
1716 const auto validStepModifierList = {Qt::NoModifier,
1717 Qt::ControlModifier,
1718 Qt::ShiftModifier};
1719
1720 for (auto subControl : subControls) {
1721
1722 const bool up = subControl == QStyle::SC_SpinBoxUp;
1723 Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown);
1724
1725 for (auto modifier : modifierList) {
1726
1727 const Qt::KeyboardModifiers modifiers(modifier);
1728
1729 const auto modifierName = modifierToName(modifier);
1730 if (modifierName.isEmpty())
1731 continue;
1732
1733 for (auto stepModifier : validStepModifierList) {
1734
1735 const auto stepModifierName = modifierToName(modifier: stepModifier);
1736 if (stepModifierName.isEmpty())
1737 continue;
1738
1739 const int steps = (modifier & stepModifier ? 10 : 1)
1740 * (up ? 1 : -1);
1741
1742 QTest::addRow(format: "%s%sWith%sKeyboardModifier",
1743 up ? "up" : "down",
1744 stepModifierName.latin1(),
1745 modifierName.latin1())
1746 << subControl
1747 << static_cast<int>(stepModifier)
1748 << modifiers
1749 << steps;
1750 }
1751 }
1752 }
1753}
1754
1755void tst_QDoubleSpinBox::stepModifierPressAndHold()
1756{
1757 QFETCH(QStyle::SubControl, subControl);
1758 QFETCH(int, stepModifier);
1759 QFETCH(Qt::KeyboardModifiers, modifiers);
1760 QFETCH(int, expectedStepModifier);
1761
1762 DoubleSpinBox spin(0);
1763 spin.setRange(min: -100.0, max: 100.0);
1764 spin.setValue(0.0);
1765
1766 QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> stepModifierStyle(
1767 new StepModifierStyle(new PressAndHoldStyle));
1768 stepModifierStyle->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier);
1769 spin.setStyle(stepModifierStyle.data());
1770
1771 QSignalSpy spy(&spin, QOverload<double>::of(ptr: &DoubleSpinBox::valueChanged));
1772
1773 spin.show();
1774 QVERIFY(QTest::qWaitForWindowExposed(&spin));
1775
1776 QStyleOptionSpinBox spinBoxStyleOption;
1777 spin.initStyleOption(option: &spinBoxStyleOption);
1778
1779 const QRect buttonRect = spin.style()->subControlRect(
1780 cc: QStyle::CC_SpinBox, opt: &spinBoxStyleOption, sc: subControl, widget: &spin);
1781
1782 QTest::mousePress(widget: &spin, button: Qt::LeftButton, stateKey: modifiers, pos: buttonRect.center());
1783 QTRY_VERIFY(spy.length() >= 3);
1784 QTest::mouseRelease(widget: &spin, button: Qt::LeftButton, stateKey: modifiers, pos: buttonRect.center());
1785
1786 const auto value = spy.last().at(i: 0);
1787 QVERIFY(value.type() == QVariant::Double);
1788 QCOMPARE(value.toDouble(), spy.length() * expectedStepModifier);
1789}
1790
1791QTEST_MAIN(tst_QDoubleSpinBox)
1792#include "tst_qdoublespinbox.moc"
1793

source code of qtbase/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp