QDateTimeEdit: with keyboardTracking off, allow values outside the range

QDateTimeEdit very aggressively prevents user input that would result in
values that are outside the dateTimeRange. While keyboardTracking is on
this makes sense, as otherwise the dateTimeChanged signal would be
emitted after each section, with a value that is outside the range.

However, this prevented users from entering a date that is allowed, but
where sections of the date are above or below the respective section in
the maximum or minimum value.

If keyboardTracking is off, QDateTimeEdit only emits the dateTimeChanged
signal at the end of editing, when focus is lost or the return key is
pressed, and then it enforces that the value is within the range anyway.
This change makes the parser ignore the range during editing if
keyboardTracking is off, thus allowing the user to enter a date where
temporary values are outside the range.

The test makes sure that we don't get signals emitted with out-of-range
values, testing both with and without keyboard tracking.

Change-Id: I00fb9f1b328a3477163f890c4618b40878657816
Fixes: QTBUG-65
Reviewed-by: Andy Shaw <andy.shaw@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Volker Hilsheimer 2020-03-17 18:07:15 +01:00
parent 30a0787907
commit 580e9eedf7
2 changed files with 128 additions and 2 deletions

View File

@ -94,8 +94,18 @@ public:
// Override QDateTimeParser:
QString displayText() const override { return edit->text(); }
QDateTime getMinimum() const override { return minimum.toDateTime(); }
QDateTime getMaximum() const override { return maximum.toDateTime(); }
QDateTime getMinimum() const override
{
if (keyboardTracking)
return minimum.toDateTime();
return QDateTimeParser::getMinimum();
}
QDateTime getMaximum() const override
{
if (keyboardTracking)
return maximum.toDateTime();
return QDateTimeParser::getMaximum();
}
QLocale locale() const override { return q_func()->locale(); }
QString getAmPmText(AmPm ap, Case cs) const override;
int cursorPosition() const override { return edit ? edit->cursorPosition() : -1; }

View File

@ -195,6 +195,8 @@ private slots:
void specialValueText();
void setRange_data();
void setRange();
void editingRanged_data();
void editingRanged();
void selectAndScrollWithKeys();
void backspaceKey();
@ -1310,6 +1312,120 @@ void tst_QDateTimeEdit::setRange()
}
}
/*
Test that a user can input a date into a ranged QDateTimeEdit or QDateEdit
where a part of date is larger than the respective part of the maximum, or
smaller than the respective part of the minimum of the range.
This test is expected to fail unless keyboard tracking of the edit is set
to off. Otherwise the changed-signal would be emitted with values outside
of the allowed range as the user types.
*/
void tst_QDateTimeEdit::editingRanged_data()
{
QTest::addColumn<QDate>("minDate");
QTest::addColumn<QTime>("minTime");
QTest::addColumn<QDate>("maxDate");
QTest::addColumn<QTime>("maxTime");
QTest::addColumn<QString>("userInput");
QTest::addColumn<QDateTime>("expected");
QTest::addRow("trivial")
<< QDate(2010, 1, 1) << QTime(9, 0)
<< QDate(2011, 12, 31) << QTime(16, 0)
<< QString::fromLatin1("311220101600")
<< QDateTime(QDate(2010, 12, 31), QTime(16, 0));
QTest::addRow("data0")
<< QDate(2010, 12, 30) << QTime(16, 0)
<< QDate(2011, 1, 2) << QTime(9, 0)
<< QString::fromLatin1("311220102359")
<< QDateTime(QDate(2010, 12, 31), QTime(23, 59));
QTest::addRow("data1")
<< QDate(2010, 12, 30) << QTime(16, 0)
<< QDate(2011, 1, 2) << QTime(9, 0)
<< QString::fromLatin1("010120111823")
<< QDateTime(QDate(2011, 1, 1), QTime(18, 23));
QTest::addRow("Out of range")
<< QDate(2010, 12, 30) << QTime(16, 0)
<< QDate(2011, 1, 2) << QTime(9, 0)
<< QString::fromLatin1("090920111823")
<< QDateTime(QDate(2011, 1, 2), QTime(9, 0));
QTest::addRow("only date")
<< QDate(2010, 12, 30) << QTime()
<< QDate(2011, 1, 2) << QTime()
<< QString::fromLatin1("01012011")
<< QDateTime(QDate(2011, 1, 1), QTime());
}
void tst_QDateTimeEdit::editingRanged()
{
QFETCH(QDate, minDate);
QFETCH(QTime, minTime);
QFETCH(QDate, maxDate);
QFETCH(QTime, maxTime);
QFETCH(QString, userInput);
QFETCH(QDateTime, expected);
QDateTimeEdit *edit;
if (minTime.isValid()) {
edit = new QDateTimeEdit;
edit->setDisplayFormat("dd.MM.yyyy hh:mm");
edit->setDateTimeRange(QDateTime(minDate, minTime), QDateTime(maxDate, maxTime));
} else {
edit = new QDateEdit;
edit->setDisplayFormat("dd.MM.yyyy");
edit->setDateRange(minDate, maxDate);
}
int callCount = 0;
connect(edit, &QDateTimeEdit::dateTimeChanged, [&](const QDateTime &dateTime) {
++callCount;
if (minTime.isValid()) {
QVERIFY(dateTime >= QDateTime(minDate, minTime));
QVERIFY(dateTime <= QDateTime(maxDate, maxTime));
} else {
QVERIFY(dateTime.date() >= minDate);
QVERIFY(dateTime.date() <= maxDate);
}
});
edit->show();
QApplication::setActiveWindow(edit);
if (!QTest::qWaitForWindowActive(edit))
QSKIP("Failed to make window active, aborting");
edit->setFocus();
// with keyboard tracking, never get a signal with an out-of-range value
edit->setKeyboardTracking(true);
QTest::keyClicks(edit, userInput);
QTest::keyClick(edit, Qt::Key_Return);
QVERIFY(callCount > 0);
// QDateTimeEdit blocks these dates from being entered - see QTBUG-65
QEXPECT_FAIL("data0", "Can't enter this date", Continue);
QEXPECT_FAIL("data1", "Can't enter this date", Continue);
QEXPECT_FAIL("Out of range", "Can't enter this date", Continue);
QEXPECT_FAIL("only date", "Can't enter this date", Continue);
QCOMPARE(edit->dateTime(), expected);
// reset
edit->clearFocus();
edit->setFocus();
callCount = 0;
edit->setKeyboardTracking(false);
QTest::keyClicks(edit, userInput);
QTest::keyClick(edit, Qt::Key_Return);
QCOMPARE(edit->dateTime(), expected);
QCOMPARE(callCount, 1);
delete edit;
}
void tst_QDateTimeEdit::wrappingTime_data()
{
QTest::addColumn<bool>("startWithMin");