aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew den Exter <andrew.den-exter@nokia.com>2012-01-18 14:43:43 +1000
committerQt by Nokia <qt-info@nokia.com>2012-01-20 07:12:19 +0100
commitc0e0f9f52bf3f89c6dd141e4d19573fa9a042628 (patch)
tree598670264f01071dda0a4ebf02ea296495414ddc
parent8ba47df57ded4104628448b35c5be02265c3d928 (diff)
Update TextInput.acceptableInput on component complete.
QValidator doesn't notify when it's validation criteria changes so is susceptible to order of evaluation issues. Deferring the initial validation will ensure validators with static criteria are correctly applied. Notification from QValidator on changes would solve this for all cases: QTBUG-23694. Task-number: QTBUG-21103 Change-Id: I920f36645fd18ce809db56b5daf73545f1d603dc Reviewed-by: Martin Jones <martin.jones@nokia.com>
-rw-r--r--src/quick/items/qquicktextinput.cpp62
-rw-r--r--src/quick/items/qquicktextinput_p_p.h17
-rw-r--r--tests/auto/qtquick2/qquicktextinput/data/validators.qml7
-rw-r--r--tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp82
4 files changed, 144 insertions, 24 deletions
diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp
index fb96709e8d..4ccf3a37fd 100644
--- a/src/quick/items/qquicktextinput.cpp
+++ b/src/quick/items/qquicktextinput.cpp
@@ -102,6 +102,7 @@ void QQuickTextInput::componentComplete()
QQuickImplicitSizeItem::componentComplete();
+ d->checkIsValid();
d->updateLayout();
updateCursorRectangle();
if (d->cursorComponent && d->cursorComponent->isReady())
@@ -948,20 +949,32 @@ void QQuickTextInput::setValidator(QValidator* v)
return;
d->m_validator = v;
- if (!d->hasAcceptableInput(d->m_text)) {
- if (d->m_validInput) {
- d->m_validInput = false;
- emit acceptableInputChanged();
- }
- } else if (!d->m_validInput) {
- d->m_validInput = true;
- emit acceptableInputChanged();
- }
+
+ if (isComponentComplete())
+ d->checkIsValid();
emit validatorChanged();
}
+
#endif // QT_NO_VALIDATOR
+void QQuickTextInputPrivate::checkIsValid()
+{
+ Q_Q(QQuickTextInput);
+
+ ValidatorState state = hasAcceptableInput(m_text);
+ m_validInput = state != InvalidInput;
+ if (state != AcceptableInput) {
+ if (m_acceptableInput) {
+ m_acceptableInput = false;
+ emit q->acceptableInputChanged();
+ }
+ } else if (!m_acceptableInput) {
+ m_acceptableInput = true;
+ emit q->acceptableInputChanged();
+ }
+}
+
/*!
\qmlproperty string QtQuick2::TextInput::inputMask
@@ -998,7 +1011,7 @@ void QQuickTextInput::setInputMask(const QString &im)
bool QQuickTextInput::hasAcceptableInput() const
{
Q_D(const QQuickTextInput);
- return d->hasAcceptableInput(d->m_text);
+ return d->hasAcceptableInput(d->m_text) == QQuickTextInputPrivate::AcceptableInput;
}
/*!
@@ -3021,12 +3034,16 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
if (m_textDirty) {
// do validation
bool wasValidInput = m_validInput;
+ bool wasAcceptable = m_acceptableInput;
m_validInput = true;
+ m_acceptableInput = true;
#ifndef QT_NO_VALIDATOR
if (m_validator) {
QString textCopy = m_text;
int cursorCopy = m_cursor;
- m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ m_validInput = state != QValidator::Invalid;
+ m_acceptableInput = state == QValidator::Acceptable;
if (m_validInput) {
if (m_text != textCopy) {
internalSetText(textCopy, cursorCopy);
@@ -3053,6 +3070,7 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
if (m_modifiedState > m_undoState)
m_modifiedState = -1;
m_validInput = true;
+ m_acceptableInput = wasAcceptable;
m_textDirty = false;
}
@@ -3065,7 +3083,7 @@ bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bo
updateDisplayText(alignmentChanged);
- if (m_validInput != wasValidInput)
+ if (m_acceptableInput != wasAcceptable)
emit q->acceptableInputChanged();
}
if (m_preeditDirty) {
@@ -3437,32 +3455,34 @@ bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
Otherwise returns false
*/
-bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
+QQuickTextInputPrivate::ValidatorState QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
{
#ifndef QT_NO_VALIDATOR
QString textCopy = str;
int cursorCopy = m_cursor;
- if (m_validator && m_validator->validate(textCopy, cursorCopy)
- != QValidator::Acceptable)
- return false;
+ if (m_validator) {
+ QValidator::State state = m_validator->validate(textCopy, cursorCopy);
+ if (state != QValidator::Acceptable)
+ return ValidatorState(state);
+ }
#endif
if (!m_maskData)
- return true;
+ return AcceptableInput;
if (str.length() != m_maxLength)
- return false;
+ return InvalidInput;
for (int i=0; i < m_maxLength; ++i) {
if (m_maskData[i].separator) {
if (str.at(i) != m_maskData[i].maskChar)
- return false;
+ return InvalidInput;
} else {
if (!isValidInput(str.at(i), m_maskData[i].maskChar))
- return false;
+ return InvalidInput;
}
}
- return true;
+ return AcceptableInput;
}
/*!
diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h
index 44ea7772c9..74f17f0116 100644
--- a/src/quick/items/qquicktextinput_p_p.h
+++ b/src/quick/items/qquicktextinput_p_p.h
@@ -125,6 +125,7 @@ public:
, m_preeditDirty(0)
, m_selDirty(0)
, m_validInput(1)
+ , m_acceptableInput(1)
, m_blinkStatus(0)
, m_passwordEchoEditing(false)
{
@@ -251,6 +252,7 @@ public:
uint m_preeditDirty : 1;
uint m_selDirty : 1;
uint m_validInput : 1;
+ uint m_acceptableInput : 1;
uint m_blinkStatus : 1;
uint m_passwordEchoEditing;
@@ -432,10 +434,23 @@ private:
inline void separate() { m_separator = true; }
+ enum ValidatorState {
+#ifndef QT_NO_VALIDATOR
+ InvalidInput = QValidator::Invalid,
+ IntermediateInput = QValidator::Intermediate,
+ AcceptableInput = QValidator::Acceptable
+#else
+ Invalid,
+ Intermediate,
+ Acceptable
+#endif
+ };
+
// masking
void parseInputMask(const QString &maskFields);
bool isValidInput(QChar key, QChar mask) const;
- bool hasAcceptableInput(const QString &text) const;
+ ValidatorState hasAcceptableInput(const QString &text) const;
+ void checkIsValid();
QString maskString(uint pos, const QString &str, bool clear = false) const;
QString clearString(uint pos, uint len) const;
QString stripString(const QString &str) const;
diff --git a/tests/auto/qtquick2/qquicktextinput/data/validators.qml b/tests/auto/qtquick2/qquicktextinput/data/validators.qml
index 0a074ce7dc..0ba87e0592 100644
--- a/tests/auto/qtquick2/qquicktextinput/data/validators.qml
+++ b/tests/auto/qtquick2/qquicktextinput/data/validators.qml
@@ -4,19 +4,26 @@ Item {
property variant intInput: intInput
property variant dblInput: dblInput
property variant strInput: strInput
+ property variant unvalidatedInput: unvalidatedInput
width: 800; height: 600;
Column{
TextInput { id: intInput;
+ property bool acceptable: acceptableInput
validator: IntValidator{top: 11; bottom: 2}
}
TextInput { id: dblInput;
+ property bool acceptable: acceptableInput
validator: DoubleValidator{top: 12.12; bottom: 2.93; decimals: 2; notation: DoubleValidator.StandardNotation}
}
TextInput { id: strInput;
+ property bool acceptable: acceptableInput
validator: RegExpValidator { regExp: /[a-zA-z]{2,4}/ }
}
+ TextInput { id: unvalidatedInput
+ property bool acceptable: acceptableInput
+ }
}
}
diff --git a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
index 8cc35f3b50..4ae5b525b1 100644
--- a/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
+++ b/tests/auto/qtquick2/qquicktextinput/tst_qquicktextinput.cpp
@@ -1573,139 +1573,217 @@ void tst_qquicktextinput::validators()
QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
QVERIFY(intInput);
+ QSignalSpy intSpy(intInput, SIGNAL(acceptableInputChanged()));
intInput->setFocus(true);
QTRY_VERIFY(intInput->hasActiveFocus());
+ QCOMPARE(intInput->hasAcceptableInput(), false);
+ QCOMPARE(intInput->property("acceptable").toBool(), false);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(intInput->text(), QLatin1String("1"));
QCOMPARE(intInput->hasAcceptableInput(), false);
+ QCOMPARE(intInput->property("acceptable").toBool(), false);
+ QCOMPARE(intSpy.count(), 0);
QTest::keyPress(&canvas, Qt::Key_2);
QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(intInput->text(), QLatin1String("1"));
QCOMPARE(intInput->hasAcceptableInput(), false);
+ QCOMPARE(intInput->property("acceptable").toBool(), false);
+ QCOMPARE(intSpy.count(), 0);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QCOMPARE(intInput->text(), QLatin1String("11"));
QCOMPARE(intInput->hasAcceptableInput(), true);
+ QCOMPARE(intInput->property("acceptable").toBool(), true);
+ QCOMPARE(intSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_0);
QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
QTest::qWait(50);
QCOMPARE(intInput->text(), QLatin1String("11"));
QCOMPARE(intInput->hasAcceptableInput(), true);
+ QCOMPARE(intInput->property("acceptable").toBool(), true);
+ QCOMPARE(intSpy.count(), 1);
QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
- QTRY_VERIFY(dblInput);
+ QVERIFY(dblInput);
+ QSignalSpy dblSpy(dblInput, SIGNAL(acceptableInputChanged()));
dblInput->setFocus(true);
QVERIFY(dblInput->hasActiveFocus() == true);
+ QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 0);
QTest::keyPress(&canvas, Qt::Key_2);
QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
+ QCOMPARE(dblSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_Period);
QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
+ QCOMPARE(dblSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
+ QCOMPARE(dblSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
+ QCOMPARE(dblSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
+ QCOMPARE(dblSpy.count(), 1);
// Ensure the validator doesn't prevent characters being removed.
dblInput->setValidator(intInput->validator());
QCOMPARE(dblInput->text(), QLatin1String("12.11"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_Backspace);
QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
// Once unacceptable input is in anything goes until it reaches an acceptable state again.
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_Backspace);
QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_Backspace);
QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_Backspace);
QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_Backspace);
QTest::keyRelease(&canvas, Qt::Key_Backspace, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
QCOMPARE(dblInput->hasAcceptableInput(), false);
+ QCOMPARE(dblInput->property("acceptable").toBool(), false);
+ QCOMPARE(dblSpy.count(), 2);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QCOMPARE(dblInput->text(), QLatin1String("11"));
+ QCOMPARE(dblInput->property("acceptable").toBool(), true);
QCOMPARE(dblInput->hasAcceptableInput(), true);
+ QCOMPARE(dblSpy.count(), 3);
QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
- QTRY_VERIFY(strInput);
+ QVERIFY(strInput);
+ QSignalSpy strSpy(strInput, SIGNAL(acceptableInputChanged()));
strInput->setFocus(true);
QVERIFY(strInput->hasActiveFocus() == true);
+ QCOMPARE(strInput->hasAcceptableInput(), false);
+ QCOMPARE(strInput->property("acceptable").toBool(), false);
QTest::keyPress(&canvas, Qt::Key_1);
QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String(""));
QCOMPARE(strInput->hasAcceptableInput(), false);
+ QCOMPARE(strInput->property("acceptable").toBool(), false);
+ QCOMPARE(strSpy.count(), 0);
QTest::keyPress(&canvas, Qt::Key_A);
QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String("a"));
QCOMPARE(strInput->hasAcceptableInput(), false);
+ QCOMPARE(strInput->property("acceptable").toBool(), false);
+ QCOMPARE(strSpy.count(), 0);
QTest::keyPress(&canvas, Qt::Key_A);
QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
QCOMPARE(strInput->hasAcceptableInput(), true);
+ QCOMPARE(strInput->property("acceptable").toBool(), true);
+ QCOMPARE(strSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_A);
QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
QCOMPARE(strInput->hasAcceptableInput(), true);
+ QCOMPARE(strInput->property("acceptable").toBool(), true);
+ QCOMPARE(strSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_A);
QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
QCOMPARE(strInput->hasAcceptableInput(), true);
+ QCOMPARE(strInput->property("acceptable").toBool(), true);
+ QCOMPARE(strSpy.count(), 1);
QTest::keyPress(&canvas, Qt::Key_A);
QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
QTest::qWait(50);
QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
QCOMPARE(strInput->hasAcceptableInput(), true);
+ QCOMPARE(strInput->property("acceptable").toBool(), true);
+ QCOMPARE(strSpy.count(), 1);
+
+ QQuickTextInput *unvalidatedInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("unvalidatedInput")));
+ QVERIFY(unvalidatedInput);
+ QSignalSpy unvalidatedSpy(unvalidatedInput, SIGNAL(acceptableInputChanged()));
+ unvalidatedInput->setFocus(true);
+ QVERIFY(unvalidatedInput->hasActiveFocus() == true);
+ QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
+ QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
+ QTest::keyPress(&canvas, Qt::Key_1);
+ QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
+ QTest::qWait(50);
+ QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1"));
+ QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
+ QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
+ QCOMPARE(unvalidatedSpy.count(), 0);
+ QTest::keyPress(&canvas, Qt::Key_A);
+ QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
+ QTest::qWait(50);
+ QTRY_COMPARE(unvalidatedInput->text(), QLatin1String("1a"));
+ QCOMPARE(unvalidatedInput->hasAcceptableInput(), true);
+ QCOMPARE(unvalidatedInput->property("acceptable").toBool(), true);
+ QCOMPARE(unvalidatedSpy.count(), 0);
}
void tst_qquicktextinput::inputMethods()