From 1df5213da69089923e697c74c6507cb96487631f Mon Sep 17 00:00:00 2001 From: David Signer Date: Mon, 7 Oct 2019 17:20:47 +0200 Subject: [PATCH 1/8] if the lineEdit contains the Null Representation it should replace the text with the current date --- .../editorwidgets/qgsdatetimeedit.sip.in | 2 ++ src/gui/editorwidgets/qgsdatetimeedit.cpp | 24 +++++++++++++++++++ src/gui/editorwidgets/qgsdatetimeedit.h | 2 ++ 3 files changed, 28 insertions(+) diff --git a/python/gui/auto_generated/editorwidgets/qgsdatetimeedit.sip.in b/python/gui/auto_generated/editorwidgets/qgsdatetimeedit.sip.in index 20fd71c1e5e..4ece357a0ce 100644 --- a/python/gui/auto_generated/editorwidgets/qgsdatetimeedit.sip.in +++ b/python/gui/auto_generated/editorwidgets/qgsdatetimeedit.sip.in @@ -115,6 +115,8 @@ Signal emitted whenever the value changes. virtual void focusOutEvent( QFocusEvent *event ); + virtual void focusInEvent( QFocusEvent *event ); + virtual void wheelEvent( QWheelEvent *event ); virtual void showEvent( QShowEvent *event ); diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index a2e08d8daa2..5caeac6a8ed 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -160,6 +160,23 @@ void QgsDateTimeEdit::focusOutEvent( QFocusEvent *event ) } } +void QgsDateTimeEdit::focusInEvent( QFocusEvent *event ) +{ + if ( mAllowNull && mIsNull && !mCurrentPressEvent ) + { + QAbstractSpinBox::focusInEvent( event ); + if ( lineEdit()->text() == QgsApplication::nullRepresentation() ) + { + displayCurrentDate(); + } + emit editingFinished(); + } + else + { + QDateTimeEdit::focusInEvent( event ); + } +} + void QgsDateTimeEdit::wheelEvent( QWheelEvent *event ) { // dateTime might have been set to minimum in calendar mode @@ -219,6 +236,13 @@ void QgsDateTimeEdit::displayNull( bool updateCalendar ) connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); } +void QgsDateTimeEdit::displayCurrentDate() +{ + disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); + QDateTimeEdit::setDateTime( QDateTime::currentDateTime() ); + connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); +} + void QgsDateTimeEdit::resetBeforeChange( int delta ) { QDateTime dt = QDateTime::currentDateTime(); diff --git a/src/gui/editorwidgets/qgsdatetimeedit.h b/src/gui/editorwidgets/qgsdatetimeedit.h index 3f8754abf59..7537f2b015a 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.h +++ b/src/gui/editorwidgets/qgsdatetimeedit.h @@ -104,6 +104,7 @@ class GUI_EXPORT QgsDateTimeEdit : public QDateTimeEdit protected: void mousePressEvent( QMouseEvent *event ) override; void focusOutEvent( QFocusEvent *event ) override; + void focusInEvent( QFocusEvent *event ) override; void wheelEvent( QWheelEvent *event ) override; void showEvent( QShowEvent *event ) override; @@ -120,6 +121,7 @@ class GUI_EXPORT QgsDateTimeEdit : public QDateTimeEdit QAction *mClearAction; void displayNull( bool updateCalendar = false ); + void displayCurrentDate(); //! reset the value to current date time void resetBeforeChange( int delta ); From 878e6e8d785d016d47a9aa974d9339ac8d24f80f Mon Sep 17 00:00:00 2001 From: David Signer Date: Mon, 7 Oct 2019 17:27:08 +0200 Subject: [PATCH 2/8] check if null instead of the value of the lineEdit --- src/gui/editorwidgets/qgsdatetimeedit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index 5caeac6a8ed..5362baff593 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -165,7 +165,7 @@ void QgsDateTimeEdit::focusInEvent( QFocusEvent *event ) if ( mAllowNull && mIsNull && !mCurrentPressEvent ) { QAbstractSpinBox::focusInEvent( event ); - if ( lineEdit()->text() == QgsApplication::nullRepresentation() ) + if ( mAllowNull && mIsNull ) { displayCurrentDate(); } From 37a2765289c8e4abc1aad3d983240de0d100a039 Mon Sep 17 00:00:00 2001 From: David Signer Date: Mon, 7 Oct 2019 17:35:52 +0200 Subject: [PATCH 3/8] go to the end of the lineEdit content, otherwise it cannot be set to the null representation value --- src/gui/editorwidgets/qgsdatetimeedit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index 5362baff593..b010879c904 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -232,6 +232,7 @@ void QgsDateTimeEdit::displayNull( bool updateCalendar ) // a date selected in calendar widget QDateTimeEdit::setDateTime( minimumDateTime() ); } + lineEdit()->setCursorPosition( lineEdit()->text().length() ); lineEdit()->setText( QgsApplication::nullRepresentation() ); connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); } From 896f26ada3acd3235ee82a76fc66269cb2015deb Mon Sep 17 00:00:00 2001 From: David Signer Date: Mon, 7 Oct 2019 17:36:50 +0200 Subject: [PATCH 4/8] use displayCurrentDate in clear for the clear button and for the setDateTime initialization it overwrites the value with the null value representation by calling displayNull after --- src/gui/editorwidgets/qgsdatetimeedit.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index b010879c904..d1eef01cc52 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -60,7 +60,7 @@ void QgsDateTimeEdit::clear() { if ( mAllowNull ) { - displayNull(); + displayCurrentDate(); // Check if it's really changed or crash, see GH #29937 if ( ! dateTime().isNull() ) @@ -76,10 +76,6 @@ void QgsDateTimeEdit::clear() disconnect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); emit dateTimeChanged( QDateTime() ); connect( this, &QDateTimeEdit::dateTimeChanged, this, &QgsDateTimeEdit::changed ); - - // otherwise, NULL is not displayed in the line edit - // this might not be the right way to do it - clearFocus(); } } @@ -280,6 +276,7 @@ void QgsDateTimeEdit::setDateTime( const QDateTime &dateTime ) if ( !dateTime.isValid() || dateTime.isNull() ) { clear(); + displayNull(); } // Check if it's really changed or crash, see GH #29937 else if ( dateTime != QgsDateTimeEdit::dateTime() ) From 8592046e661686d851a32ff83d1381b095ed905f Mon Sep 17 00:00:00 2001 From: David Signer Date: Tue, 8 Oct 2019 07:40:37 +0200 Subject: [PATCH 5/8] do not trigger editing finished in inFocus event like it does in outFocus event function docs in header --- src/gui/editorwidgets/qgsdatetimeedit.cpp | 1 - src/gui/editorwidgets/qgsdatetimeedit.h | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index d1eef01cc52..a9cfe1fad2c 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -165,7 +165,6 @@ void QgsDateTimeEdit::focusInEvent( QFocusEvent *event ) { displayCurrentDate(); } - emit editingFinished(); } else { diff --git a/src/gui/editorwidgets/qgsdatetimeedit.h b/src/gui/editorwidgets/qgsdatetimeedit.h index 7537f2b015a..007da55d942 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.h +++ b/src/gui/editorwidgets/qgsdatetimeedit.h @@ -120,7 +120,15 @@ class GUI_EXPORT QgsDateTimeEdit : public QDateTimeEdit QString mOriginalStyleSheet = QString(); QAction *mClearAction; + /** + * write the null value representation to the line edit without changing the value + * \param updateCalendar Flag if calendar is open and minimum date needs to be set + */ void displayNull( bool updateCalendar = false ); + + /** + * write the current date into the line edit without changing the value + */ void displayCurrentDate(); //! reset the value to current date time From 7b4401e343c180e14f34b8561a019ae5508334d3 Mon Sep 17 00:00:00 2001 From: David Signer Date: Tue, 8 Oct 2019 07:50:05 +0200 Subject: [PATCH 6/8] widget initialization minor fix --- tests/src/gui/testqgsrangewidgetwrapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/gui/testqgsrangewidgetwrapper.cpp b/tests/src/gui/testqgsrangewidgetwrapper.cpp index 92f71e28609..dc2ad50c88b 100644 --- a/tests/src/gui/testqgsrangewidgetwrapper.cpp +++ b/tests/src/gui/testqgsrangewidgetwrapper.cpp @@ -348,14 +348,14 @@ void TestQgsRangeWidgetWrapper::test_focus() QgsDoubleSpinBox *editor1 = qobject_cast( widget1->createWidget( w ) ); QVERIFY( editor1 ); widget1->initWidget( editor1 ); - widget3->setValue( QVariant( QVariant::Double ) ); + widget1->setValue( QVariant( QVariant::Double ) ); //QgsDoubleSpinBox widget2->setConfig( cfg ); QgsDoubleSpinBox *editor2 = qobject_cast( widget2->createWidget( w ) ); QVERIFY( editor2 ); widget2->initWidget( editor2 ); - widget3->setValue( QVariant( QVariant::Double ) ); + widget2->setValue( QVariant( QVariant::Double ) ); //QgsSpinBox widget3->setConfig( cfg ); From fdbc630c53bd636256acafc13616c302d66b15a5 Mon Sep 17 00:00:00 2001 From: David Signer Date: Tue, 8 Oct 2019 08:43:05 +0200 Subject: [PATCH 7/8] tests for datetime widget regarding focus and null value representers --- src/gui/editorwidgets/qgsdatetimeedit.h | 2 + .../editorwidgets/qgsdatetimeeditwrapper.h | 4 + tests/src/gui/testqgsdatetimeedit.cpp | 147 ++++++++++++++++++ 3 files changed, 153 insertions(+) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.h b/src/gui/editorwidgets/qgsdatetimeedit.h index 007da55d942..810fac8edf9 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.h +++ b/src/gui/editorwidgets/qgsdatetimeedit.h @@ -149,6 +149,8 @@ class GUI_EXPORT QgsDateTimeEdit : public QDateTimeEdit { setMinimumDateTime( QDateTime::fromString( QStringLiteral( "0100-01-01" ), Qt::ISODate ) ); } + + friend class TestQgsDateTimeEdit; }; #endif // QGSDATETIMEEDIT_H diff --git a/src/gui/editorwidgets/qgsdatetimeeditwrapper.h b/src/gui/editorwidgets/qgsdatetimeeditwrapper.h index 5a37be5c96b..94a29da4b06 100644 --- a/src/gui/editorwidgets/qgsdatetimeeditwrapper.h +++ b/src/gui/editorwidgets/qgsdatetimeeditwrapper.h @@ -24,6 +24,7 @@ SIP_NO_FILE class QgsDateTimeEdit; +class TestQgsDateTimeEdit; /** * \ingroup gui @@ -77,6 +78,9 @@ class GUI_EXPORT QgsDateTimeEditWrapper : public QgsEditorWidgetWrapper private: void updateValues( const QVariant &value, const QVariantList & = QVariantList() ) override; + + + friend class TestQgsDateTimeEdit; }; #endif // QGSDATETIMEEDITWRAPPER_H diff --git a/tests/src/gui/testqgsdatetimeedit.cpp b/tests/src/gui/testqgsdatetimeedit.cpp index 2bc9ef08387..f41c14489e6 100644 --- a/tests/src/gui/testqgsdatetimeedit.cpp +++ b/tests/src/gui/testqgsdatetimeedit.cpp @@ -18,6 +18,8 @@ #include "qdatetime.h" #include +#include +#include class TestQgsDateTimeEdit: public QObject { @@ -29,6 +31,13 @@ class TestQgsDateTimeEdit: public QObject void cleanup(); // will be called after every testfunction. void nullValues(); + void focus(); + + private: + std::unique_ptr widget1; // For field 0 + std::unique_ptr widget2; // For field 1 + std::unique_ptr widget3; // For field 2 + std::unique_ptr vl; }; @@ -42,6 +51,27 @@ void TestQgsDateTimeEdit::cleanupTestCase() void TestQgsDateTimeEdit::init() { + + vl = qgis::make_unique( QStringLiteral( "Point?crs=epsg:4326" ), + QStringLiteral( "myvl" ), + QLatin1Literal( "memory" ) ); + + // add fields + QList fields; + fields.append( QgsField( "date1", QVariant::Date ) ); + fields.append( QgsField( "date2", QVariant::Date ) ); + fields.append( QgsField( "date3", QVariant::Date ) ); + vl->dataProvider()->addAttributes( fields ); + vl->updateFields(); + QVERIFY( vl.get() ); + QVERIFY( vl->isValid() ); + + widget1 = qgis::make_unique( vl.get(), 0, nullptr, nullptr ); + widget2 = qgis::make_unique( vl.get(), 1, nullptr, nullptr ); + widget3 = qgis::make_unique( vl.get(), 2, nullptr, nullptr ); + QVERIFY( widget1.get() ); + QVERIFY( widget2.get() ); + QVERIFY( widget3.get() ); } void TestQgsDateTimeEdit::cleanup() @@ -79,5 +109,122 @@ void TestQgsDateTimeEdit::nullValues() delete timeEdit; } +void TestQgsDateTimeEdit::focus() +{ + QgsApplication::setNullRepresentation( QString( "nope" ) ); + QWidget *w = new QWidget(); //required for focus events + QApplication::setActiveWindow( w ); + + QVariantMap cfg; + cfg.insert( QStringLiteral( "AllowNull" ), true ); + + widget1->setConfig( cfg ); + QgsDateTimeEdit *dateedit1 = qobject_cast( widget1->createWidget( w ) ); + QVERIFY( dateedit1 ); + widget1->initWidget( dateedit1 ); + widget1->setValue( QVariant::Date ); + + widget2->setConfig( cfg ); + QgsDateTimeEdit *dateedit2 = qobject_cast( widget2->createWidget( w ) ); + QVERIFY( dateedit2 ); + widget2->initWidget( dateedit2 ); + widget2->setValue( QVariant::Date ); + + widget3->setConfig( cfg ); + QgsDateTimeEdit *dateedit3 = qobject_cast( widget3->createWidget( w ) ); + QVERIFY( dateedit3 ); + widget3->initWidget( dateedit3 ); + widget3->setValue( QVariant::Date ); + + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( !dateedit1->hasFocus() ); + QVERIFY( !dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit2->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit1->setFocus(); + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( dateedit1->hasFocus() ); + QVERIFY( !dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QCOMPARE( dateedit2->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit2->setFocus(); + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( !dateedit1->hasFocus() ); + QVERIFY( dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit2->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit3->setFocus(); + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( !dateedit1->hasFocus() ); + QVERIFY( !dateedit2->hasFocus() ); + QVERIFY( dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit2->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit3->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + + dateedit1->setFocus(); + dateedit1->setDateTime( QDateTime::fromString( QStringLiteral( "1955-11-12" ), QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QVERIFY( !widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( dateedit1->hasFocus() ); + QVERIFY( !dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "1955-11-12" ) ); + QCOMPARE( dateedit2->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit2->setFocus(); + QVERIFY( !widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( !dateedit1->hasFocus() ); + QVERIFY( dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "1955-11-12" ) ); + QCOMPARE( dateedit2->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit1->setFocus(); + dateedit1->clear(); + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( dateedit1->hasFocus() ); + QVERIFY( !dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QCOMPARE( dateedit2->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); + + dateedit2->setFocus(); + QVERIFY( widget1->value().isNull() ); + QVERIFY( widget2->value().isNull() ); + QVERIFY( widget3->value().isNull() ); + QVERIFY( !dateedit1->hasFocus() ); + QVERIFY( dateedit2->hasFocus() ); + QVERIFY( !dateedit3->hasFocus() ); + QCOMPARE( dateedit1->text(), QStringLiteral( "nope" ) ); + QCOMPARE( dateedit2->text(), QDateTime::currentDateTime().toString( QgsDateTimeFieldFormatter::DATE_FORMAT ) ); + QCOMPARE( dateedit3->text(), QStringLiteral( "nope" ) ); +} + QGSTEST_MAIN( TestQgsDateTimeEdit ) #include "testqgsdatetimeedit.moc" From 46280fb36fa73ab652292dc11af8dbb06cc6e331 Mon Sep 17 00:00:00 2001 From: David Signer Date: Tue, 8 Oct 2019 09:29:22 +0200 Subject: [PATCH 8/8] avoid double check --- src/gui/editorwidgets/qgsdatetimeedit.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/gui/editorwidgets/qgsdatetimeedit.cpp b/src/gui/editorwidgets/qgsdatetimeedit.cpp index a9cfe1fad2c..46fc99a4a1e 100644 --- a/src/gui/editorwidgets/qgsdatetimeedit.cpp +++ b/src/gui/editorwidgets/qgsdatetimeedit.cpp @@ -161,10 +161,8 @@ void QgsDateTimeEdit::focusInEvent( QFocusEvent *event ) if ( mAllowNull && mIsNull && !mCurrentPressEvent ) { QAbstractSpinBox::focusInEvent( event ); - if ( mAllowNull && mIsNull ) - { - displayCurrentDate(); - } + + displayCurrentDate(); } else {