diff options
author | Oliver Eftevaag <oliver.eftevaag@qt.io> | 2021-06-11 14:06:57 +0200 |
---|---|---|
committer | Oliver Eftevaag <oliver.eftevaag@qt.io> | 2021-06-17 15:17:42 +0200 |
commit | f8db2b996f339ad7e0754cd232f1e71ebecf6367 (patch) | |
tree | 96b70637342392413f4870a1906350ff0bf416e3 | |
parent | 74ddeb461ef3d80d0127380e2fab4047f85bdba9 (diff) |
QQuickComboBox: fix acceptableInput being wrong if no validator was set
This patch fixes an issue with hasAcceptableInput(), if the property
would be read before the contentItem had been set by the qml engine.
This would cause hasAcceptableInput to return false by default, even
though the default value is supposed to be true, if no validators or
inputMasks are being used.
The solution that I've chosen, is to give the QQuickComboBox its own
acceptableInput variable, and connect the contentItem's
acceptableInputChanged() signal to a function that polls for the
contentItem property, and updates its variable accordingly.
Fixes: QTBUG-94307
Pick-to: 6.2 6.1 5.15
Change-Id: I587d76162e75544a7ed1df9e3b9104bd73013bb0
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickcombobox.cpp | 30 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_combobox.qml | 40 |
2 files changed, 67 insertions, 3 deletions
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp index c94fbee8..bad62b28 100644 --- a/src/quicktemplates2/qquickcombobox.cpp +++ b/src/quicktemplates2/qquickcombobox.cpp @@ -238,6 +238,7 @@ public: void updateCurrentText(); void updateCurrentValue(); void updateCurrentTextAndValue(); + void updateAcceptableInput(); bool isValidIndex(int index) const; @@ -302,6 +303,7 @@ public: QQmlComponent *delegate = nullptr; QQuickDeferredPointer<QQuickItem> indicator; QQuickDeferredPointer<QQuickPopup> popup; + bool m_acceptableInput = true; struct ExtraData { bool editable = false; @@ -499,6 +501,26 @@ void QQuickComboBoxPrivate::updateCurrentTextAndValue() updateCurrentValue(); } +void QQuickComboBoxPrivate::updateAcceptableInput() +{ + Q_Q(QQuickComboBox); + + if (!contentItem) + return; + + const QQuickTextInput *textInputContentItem = qobject_cast<QQuickTextInput *>(contentItem); + + if (!textInputContentItem) + return; + + const bool newValue = textInputContentItem->hasAcceptableInput(); + + if (m_acceptableInput != newValue) { + m_acceptableInput = newValue; + emit q->acceptableInputChanged(); + } +} + bool QQuickComboBoxPrivate::isValidIndex(int index) const { return delegateModel && index >= 0 && index < delegateModel->count(); @@ -1577,7 +1599,7 @@ bool QQuickComboBox::isInputMethodComposing() const bool QQuickComboBox::hasAcceptableInput() const { Q_D(const QQuickComboBox); - return d->contentItem && d->contentItem->property("acceptableInput").toBool(); + return d->m_acceptableInput; } /*! @@ -2110,7 +2132,7 @@ void QQuickComboBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) QObjectPrivate::disconnect(oldInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput); QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText); disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged); - disconnect(oldInput, &QQuickTextInput::acceptableInputChanged, this, &QQuickComboBox::acceptableInputChanged); + QObjectPrivate::disconnect(oldInput, &QQuickTextInput::acceptableInputChanged, d, &QQuickComboBoxPrivate::updateAcceptableInput); } } if (newItem && isEditable()) { @@ -2119,12 +2141,14 @@ void QQuickComboBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) QObjectPrivate::connect(newInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput); QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText); connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged); - connect(newInput, &QQuickTextInput::acceptableInputChanged, this, &QQuickComboBox::acceptableInputChanged); + QObjectPrivate::connect(newInput, &QQuickTextInput::acceptableInputChanged, d, &QQuickComboBoxPrivate::updateAcceptableInput); } #if QT_CONFIG(cursor) newItem->setCursor(Qt::IBeamCursor); #endif } + + d->updateAcceptableInput(); } void QQuickComboBox::localeChange(const QLocale &newLocale, const QLocale &oldLocale) diff --git a/tests/auto/controls/data/tst_combobox.qml b/tests/auto/controls/data/tst_combobox.qml index 460c490a..5359f63c 100644 --- a/tests/auto/controls/data/tst_combobox.qml +++ b/tests/auto/controls/data/tst_combobox.qml @@ -157,6 +157,7 @@ TestCase { verify(control.delegate) verify(control.indicator) verify(control.popup) + verify(control.acceptableInput) compare(control.inputMethodHints, Qt.ImhNoPredictiveText) } @@ -2240,4 +2241,43 @@ TestCase { verify(control.activeFocus) verify(control.contentItem.focus) } + + Component { + id: intValidatorComponent + IntValidator { + bottom: 0 + top: 255 + } + } + + function test_acceptableInput_QTBUG_94307() { + let items = [ + { text: "A" }, + { text: "2" }, + { text: "3" } + ] + let control = createTemporaryObject(comboBox, testCase, {model: items, editable: true}) + verify(control) + + verify(control.acceptableInput) + compare(control.displayText, "A") + + let acceptableInputSpy = signalSpy.createObject(control, {target: control, signalName: "acceptableInputChanged"}) + verify(acceptableInputSpy.valid) + + let intValidator = intValidatorComponent.createObject(testCase) + verify(intValidator) + + control.validator = intValidator + + compare(acceptableInputSpy.count, 1) + compare(control.displayText, "A") + compare(control.acceptableInput, false) + + control.currentIndex = 1 + + compare(acceptableInputSpy.count, 2) + compare(control.displayText, "2") + compare(control.acceptableInput, true) + } } |