Fix redundant emission of editingFinished from QLineEdit with Return key

Current implementation of QLineEdit uses the "edited" bit-field flag to
prevent unnecessary emissions of editingFinished() when a line edit
loses focus but its contents have not changed since the last time the
signal was emitted; however, this flag is only cleared when the signal
is fired due to focus loss and not when the Return/Enter key is pressed.
This causes an unexpected double emission of the signal when focus is
lost following a press of the Return/Enter key if the line edit's text
was not further altered between the two actions.

This change includes the Return/Enter press as a trigger for clearing
the "edited" flag to make editingFinished()'s behavior more consistent
and expected. Prevents slots in user code from triggering twice in
situations where the line edit's current contents have already been
handled, but still allows the end-user to force an emission of the
signal via Return/Enter.

The effect of the "edited" flag on the signals behavior has also been
noted in the signal description as it was previously omitted.

[ChangeLog][QtWidgets][QLineEdit][Behavior Change] Pressing the
Return/Enter key in a QLineEdit will now also prevent editingFinished()
from firing due to focus loss if the contents of the line edit have not
changed since the last time the signal was emitted.

See - https://forum.qt.io/topic/116902/

Change-Id: I11aadd45341337b7852da8cf5802c7c9efdd614d
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Christian Heimlich 2020-08-31 02:01:58 -04:00
parent 9c5698a8fc
commit e1cf5b2348
5 changed files with 84 additions and 5 deletions

View File

@ -1691,8 +1691,11 @@ void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e)
/*!
\fn void QLineEdit::editingFinished()
This signal is emitted when the Return or Enter key is pressed or
the line edit loses focus. Note that if there is a validator() or
This signal is emitted when the Return or Enter key is pressed, or
if the line edit loses focus and its contents have changed since the
last time this signal was emitted.
Note that if there is a validator() or
inputMask() set on the line edit and enter/return is pressed, the
editingFinished() signal will only be emitted if the input follows
the inputMask() and the validator() returns QValidator::Acceptable.

View File

@ -263,6 +263,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_updateNeeded(const QRect &))
Q_PRIVATE_SLOT(d_func(), void _q_textChanged(const QString &))
Q_PRIVATE_SLOT(d_func(), void _q_clearButtonClicked())
Q_PRIVATE_SLOT(d_func(), void _q_controlEditingFinished())
};
QT_END_NAMESPACE

View File

@ -193,10 +193,8 @@ void QLineEditPrivate::init(const QString& txt)
q, SLOT(_q_cursorPositionChanged(int,int)));
QObject::connect(control, SIGNAL(selectionChanged()),
q, SLOT(_q_selectionChanged()));
QObject::connect(control, SIGNAL(accepted()),
q, SIGNAL(returnPressed()));
QObject::connect(control, SIGNAL(editingFinished()),
q, SIGNAL(editingFinished()));
q, SLOT(_q_controlEditingFinished()));
#ifdef QT_KEYPAD_NAVIGATION
QObject::connect(control, SIGNAL(editFocusChange(bool)),
q, SLOT(_q_editFocusChange(bool)));
@ -485,6 +483,14 @@ void QLineEditPrivate::_q_clearButtonClicked()
}
}
void QLineEditPrivate::_q_controlEditingFinished()
{
Q_Q(QLineEdit);
edited = false;
emit q->returnPressed();
emit q->editingFinished();
}
QLineEditPrivate::SideWidgetParameters QLineEditPrivate::sideWidgetParameters() const
{
Q_Q(const QLineEdit);

View File

@ -233,6 +233,7 @@ public:
#endif
void _q_textChanged(const QString &);
void _q_clearButtonClicked();
void _q_controlEditingFinished();
QMargins textMargins; // use effectiveTextMargins() in case of icon.

View File

@ -250,6 +250,8 @@ private slots:
void textMargin_data();
void textMargin();
void returnKeyClearsEditedFlag();
// task-specific tests:
void task180999_focus();
void task174640_editingFinished();
@ -3586,6 +3588,72 @@ void tst_QLineEdit::textMargin()
QTRY_COMPARE(testWidget.cursorPosition(), cursorPosition);
}
void tst_QLineEdit::returnKeyClearsEditedFlag()
{
/* Tests that pressing enter within the line edit correctly clears
the "edited" flag, preventing a redundant emission of
editingFinished() when its focus is dropped after no further
edits */
QLineEdit testWidget;
QSignalSpy leSpy(&testWidget, &QLineEdit::editingFinished);
QVERIFY(leSpy.isValid());
// Prepare widget for testing
testWidget.setFocus();
centerOnScreen(&testWidget);
testWidget.show();
testWidget.raise();
QVERIFY(QTest::qWaitForWindowExposed(&testWidget));
QTRY_VERIFY(testWidget.hasFocus());
// Focus drop with no edits shouldn't emit signal, edited flag == false
testWidget.clearFocus(); // Signal not emitted
QVERIFY(!testWidget.hasFocus());
QCOMPARE(leSpy.count(), 0);
// Focus drop after edits should emit signal, edited flag == true
testWidget.setFocus();
QTRY_VERIFY(testWidget.hasFocus());
QTest::keyClicks(&testWidget, "edit1 "); // edited flag set
testWidget.clearFocus(); // edited flag cleared, signal emitted
QVERIFY(!testWidget.hasFocus());
QCOMPARE(leSpy.count(), 1);
// Only text related keys should set edited flag
testWidget.setFocus();
QTRY_VERIFY(testWidget.hasFocus());
QTest::keyClick(&testWidget, Qt::Key_Left);
QTest::keyClick(&testWidget, Qt::Key_Alt);
QTest::keyClick(&testWidget, Qt::Key_PageUp);
testWidget.clearFocus(); // Signal not emitted
QVERIFY(!testWidget.hasFocus());
QCOMPARE(leSpy.count(), 1); // No change
// Return should always emit signal
testWidget.setFocus();
QTRY_VERIFY(testWidget.hasFocus());
QTest::keyClick(&testWidget, Qt::Key_Return); /* Without edits,
signal emitted,
edited flag cleared */
QCOMPARE(leSpy.count(), 2);
QTest::keyClicks(&testWidget, "edit2 "); // edited flag set
QTest::keyClick(&testWidget, Qt::Key_Return); /* With edits,
signal emitted,
edited flag cleared */
QCOMPARE(leSpy.count(), 3);
/* After editing the line edit following a Return key press with a
focus drop should not emit signal a second time since Return now
clears the edited flag */
QTest::keyClicks(&testWidget, "edit3 "); // edited flag set
QTest::keyClick(&testWidget, Qt::Key_Return); /* signal emitted,
edited flag cleared */
QCOMPARE(leSpy.count(), 4);
testWidget.clearFocus(); // Signal not emitted since edited == false
QVERIFY(!testWidget.hasFocus());
QCOMPARE(leSpy.count(), 4); // No change
}
#ifndef QT_NO_CURSOR
void tst_QLineEdit::cursor()
{