From e1cf5b23481af9e9646f9aeb0e45a46e31e8684d Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Mon, 31 Aug 2020 02:01:58 -0400 Subject: 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 --- src/widgets/widgets/qlineedit.cpp | 7 ++- src/widgets/widgets/qlineedit.h | 1 + src/widgets/widgets/qlineedit_p.cpp | 12 +++- src/widgets/widgets/qlineedit_p.h | 1 + .../widgets/widgets/qlineedit/tst_qlineedit.cpp | 68 ++++++++++++++++++++++ 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 07985cf966..9f1f00ced7 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -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. diff --git a/src/widgets/widgets/qlineedit.h b/src/widgets/widgets/qlineedit.h index f09d62ec78..b4f6641b86 100644 --- a/src/widgets/widgets/qlineedit.h +++ b/src/widgets/widgets/qlineedit.h @@ -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 diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp index 9fe5b5af3f..1839941036 100644 --- a/src/widgets/widgets/qlineedit_p.cpp +++ b/src/widgets/widgets/qlineedit_p.cpp @@ -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); diff --git a/src/widgets/widgets/qlineedit_p.h b/src/widgets/widgets/qlineedit_p.h index 049f7a3bdf..936cf2d088 100644 --- a/src/widgets/widgets/qlineedit_p.h +++ b/src/widgets/widgets/qlineedit_p.h @@ -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. diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index 87be592e2b..547bc49199 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -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() { -- cgit v1.2.3