Fix no-op emission of QComboBox::currentTextChanged

currentTextChanged is emitted when the current index changes
and the current text doesn't.
This can be the case, if
- old and new index have identical text values
- an item is removed below the current index

[ChangeLog][Widgets][QComboBox] emit currentTextChanged only,
if currentText changes.

Add a corresponding test in tst_QComboBox::currentText().

Fixes: QTBUG-113717
Change-Id: I847874f0792b29a2841e50bb82d06ad496fb02c3
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit 9195438a5fd88c676b0fc4abd429bcffa6f97e24)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Axel Spoerl 2023-06-07 14:17:14 +02:00 committed by Qt Cherry-pick Bot
parent b49593c0c8
commit 56598b65fa
3 changed files with 60 additions and 18 deletions

View File

@ -904,8 +904,11 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const
\fn void QComboBox::currentTextChanged(const QString &text)
\since 5.0
This signal is sent whenever currentText changes. The new value
is passed as \a text.
This signal is emitted whenever currentText changes.
The new value is passed as \a text.
\note It is not emitted, if currentText remains the same,
even if currentIndex changes.
*/
/*!
@ -1071,7 +1074,7 @@ void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIn
lineEdit->setText(text);
updateLineEditGeometry();
} else {
emit q->currentTextChanged(text);
updateCurrentText(text);
}
q->update();
#if QT_CONFIG(accessibility)
@ -1382,7 +1385,7 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
emit q->currentIndexChanged(index.row());
// signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
if (!lineEdit)
emit q->currentTextChanged(text);
updateCurrentText(text);
#if QT_CONFIG(accessibility)
QAccessibleValueChangeEvent event(q, text);
QAccessible::updateAccessibility(&event);
@ -2848,6 +2851,15 @@ void QComboBoxPrivate::doHidePopup()
_q_resetButton();
}
void QComboBoxPrivate::updateCurrentText(const QString &text)
{
if (text == currentText)
return;
currentText = text;
emit q_func()->currentTextChanged(text);
}
/*!
Clears the combobox, removing all items.

View File

@ -357,6 +357,7 @@ public:
void updateFocusPolicy();
void showPopupFromMouseEvent(QMouseEvent *e);
void doHidePopup();
void updateCurrentText(const QString &text);
#ifdef Q_OS_MAC
void cleanupNativePopup();
@ -382,6 +383,7 @@ public:
QPersistentModelIndex currentIndex;
QPersistentModelIndex root;
QString placeholderText;
QString currentText;
QRect hoverRect;
QSize iconSize;
mutable QSize minimumSizeHint;

View File

@ -1503,42 +1503,70 @@ void tst_QComboBox::currentTextChanged()
testWidget->addItems(QStringList() << "foo" << "bar");
QCOMPARE(testWidget->count(), 2);
QSignalSpy spy(testWidget, SIGNAL(currentTextChanged(QString)));
QSignalSpy textChangedSpy(testWidget, &QComboBox::currentTextChanged);
testWidget->setEditable(editable);
// set text in list
testWidget->setCurrentIndex(0);
QCOMPARE(testWidget->currentIndex(), 0);
spy.clear();
textChangedSpy.clear();
testWidget->setCurrentText(QString("bar"));
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar"));
QCOMPARE(textChangedSpy.size(), 1);
QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("bar"));
// set text not in list
testWidget->setCurrentIndex(0);
QCOMPARE(testWidget->currentIndex(), 0);
spy.clear();
textChangedSpy.clear();
testWidget->setCurrentText(QString("qt"));
if (editable) {
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("qt"));
QCOMPARE(textChangedSpy.size(), 1);
QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("qt"));
} else {
QCOMPARE(spy.size(), 0);
QCOMPARE(textChangedSpy.size(), 0);
}
// item changed
testWidget->setCurrentIndex(0);
QCOMPARE(testWidget->currentIndex(), 0);
spy.clear();
textChangedSpy.clear();
testWidget->setItemText(0, QString("ape"));
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("ape"));
QCOMPARE(textChangedSpy.size(), 1);
QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("ape"));
// change it back
spy.clear();
textChangedSpy.clear();
testWidget->setItemText(0, QString("foo"));
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("foo"));
QCOMPARE(textChangedSpy.size(), 1);
QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("foo"));
// currentIndexChanged vs. currentTextChanged
testWidget->clear();
testWidget->addItems(QStringList() << "first" << "second" << "third" << "fourth" << "fourth");
testWidget->setCurrentIndex(4);
textChangedSpy.clear();
QSignalSpy indexChangedSpy(testWidget, &QComboBox::currentIndexChanged);
// Index change w/o text change
testWidget->removeItem(3);
QCOMPARE(textChangedSpy.count(), 0);
QCOMPARE(indexChangedSpy.count(), 1);
// Index and text change
testWidget->setCurrentIndex(0);
QCOMPARE(textChangedSpy.count(), 1);
QCOMPARE(indexChangedSpy.count(), 2);
// remove item above current index
testWidget->removeItem(2);
QCOMPARE(textChangedSpy.count(), 1);
QCOMPARE(indexChangedSpy.count(), 2);
// Text change w/o index change
testWidget->setItemText(0, "first class");
QCOMPARE(textChangedSpy.count(), 2);
QCOMPARE(indexChangedSpy.count(), 2);
}
void tst_QComboBox::editTextChanged()