diff --git a/python/gui/editorwidgets/qgsdoublespinbox.sip b/python/gui/editorwidgets/qgsdoublespinbox.sip index 6fac256ff6b..1666a02ef71 100644 --- a/python/gui/editorwidgets/qgsdoublespinbox.sip +++ b/python/gui/editorwidgets/qgsdoublespinbox.sip @@ -1,3 +1,9 @@ +/** \ingroup gui + * @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value. + * The clear value can be either the minimum or the maiximum value of the spin box or a custom value. + * This value can then be handled by a special value text. + */ + class QgsDoubleSpinBox : QDoubleSpinBox { %TypeHeaderCode @@ -5,18 +11,30 @@ class QgsDoubleSpinBox : QDoubleSpinBox %End public: + + //! Behaviour when widget is cleared. enum ClearValueMode { - MinimumValue, - MaximumValue, - CustomValue + MinimumValue, //!< Reset value to minimum() + MaximumValue, //!< Reset value to maximum() + CustomValue, //!< Reset value to custom value (see setClearValue() ) }; + /** Constructor for QgsDoubleSpinBox. + * @param parent parent widget + */ explicit QgsDoubleSpinBox( QWidget *parent /TransferThis/ = 0 ); - //! determines if the widget will show a clear button - //! @note the clear button will set the widget to its minimum value + /** Sets whether the widget will show a clear button. The clear button + * allows users to reset the widget to a default or empty state. + * @param showClearButton set to true to show the clear button, or false to hide it + * @see showClearButton() + */ void setShowClearButton( const bool showClearButton ); + + /** Returns whether the widget is showing a clear button. + * @see setShowClearButton() + */ bool showClearButton() const; /** Sets if the widget will allow entry of simple expressions, which are @@ -25,6 +43,7 @@ class QgsDoubleSpinBox : QDoubleSpinBox * @note added in QGIS 2.7 */ void setExpressionsEnabled( const bool enabled ); + /** Returns whether the widget will allow entry of simple expressions, which are * evaluated and then discarded. * @returns true if spin box allows expression entry @@ -36,26 +55,29 @@ class QgsDoubleSpinBox : QDoubleSpinBox virtual void clear(); /** - * @brief setClearValue defines the clear value as a custom value and will automatically set the clear value mode to CustomValue + * Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue. * @param customValue defines the numerical value used as the clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. + * @see setClearValue() */ void setClearValue( double customValue, const QString& clearValueText = QString() ); + /** - * @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value + * Defines if the clear value should be the minimum or maximum values of the widget or a custom value. * @param mode mode to user for clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. */ void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() ); - //! returns the value used when clear() is called. + /** Returns the value used when clear() is called. + * @see setClearValue() + */ double clearValue() const; virtual double valueFromText( const QString & text ) const; virtual QValidator::State validate( QString & input, int & pos ) const; protected: - virtual void resizeEvent( QResizeEvent* event ); virtual void changeEvent( QEvent* event ); virtual void paintEvent( QPaintEvent* event ); }; diff --git a/python/gui/editorwidgets/qgsspinbox.sip b/python/gui/editorwidgets/qgsspinbox.sip index 09e8b65d067..d5606414d98 100644 --- a/python/gui/editorwidgets/qgsspinbox.sip +++ b/python/gui/editorwidgets/qgsspinbox.sip @@ -1,3 +1,9 @@ +/** \ingroup gui + * @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value. + * The clear value can be either the minimum or the maiximum value of the spin box or a custom value. + * This value can then be handled by a special value text. + */ + class QgsSpinBox : QSpinBox { %TypeHeaderCode @@ -5,18 +11,30 @@ class QgsSpinBox : QSpinBox %End public: + + //! Behaviour when widget is cleared. enum ClearValueMode { - MinimumValue, - MaximumValue, - CustomValue + MinimumValue, //!< Reset value to minimum() + MaximumValue, //!< Reset value to maximum() + CustomValue, //!< Reset value to custom value (see setClearValue() ) }; + /** Constructor for QgsSpinBox. + * @param parent parent widget + */ explicit QgsSpinBox( QWidget *parent /TransferThis/ = 0 ); - //! determines if the widget will show a clear button - //! @note the clear button will set the widget to its minimum value + /** Sets whether the widget will show a clear button. The clear button + * allows users to reset the widget to a default or empty state. + * @param showClearButton set to true to show the clear button, or false to hide it + * @see showClearButton() + */ void setShowClearButton( const bool showClearButton ); + + /** Returns whether the widget is showing a clear button. + * @see setShowClearButton() + */ bool showClearButton() const; /** Sets if the widget will allow entry of simple expressions, which are @@ -25,6 +43,7 @@ class QgsSpinBox : QSpinBox * @note added in QGIS 2.7 */ void setExpressionsEnabled( const bool enabled ); + /** Returns whether the widget will allow entry of simple expressions, which are * evaluated and then discarded. * @returns true if spin box allows expression entry @@ -36,26 +55,30 @@ class QgsSpinBox : QSpinBox virtual void clear(); /** - * @brief setClearValue defines the clear value for the widget and will automatically set the clear value mode to CustomValue + * Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue. * @param customValue defines the numerical value used as the clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. + * @see setClearValue() */ void setClearValue( int customValue, const QString& clearValueText = QString() ); + /** - * @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value + * Defines if the clear value should be the minimum or maximum values of the widget or a custom value. * @param mode mode to user for clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. */ void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() ); - //! returns the value used when clear() is called. + /** Returns the value used when clear() is called. + * @see setClearValue() + */ int clearValue() const; virtual int valueFromText( const QString & text ) const; virtual QValidator::State validate( QString & input, int & pos ) const; protected: - virtual void resizeEvent( QResizeEvent* event ); + virtual void changeEvent( QEvent* event ); virtual void paintEvent( QPaintEvent* event ); }; diff --git a/python/gui/qgsfilterlineedit.sip b/python/gui/qgsfilterlineedit.sip index 8a1e53e03c7..9304e639bd2 100644 --- a/python/gui/qgsfilterlineedit.sip +++ b/python/gui/qgsfilterlineedit.sip @@ -1,46 +1,138 @@ - -/** LineEdit with builtin clear button - */ +/** \class QgsFilterLineEdit + * \ingroup gui + * QLineEdit subclass with built in support for clearing the widget's value and + * handling custom null value representations. + * + * When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used + * instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged() + * signal should be used instead of textChanged(). + **/ class QgsFilterLineEdit : QLineEdit { %TypeHeaderCode #include %End public: + + //! Behaviour when clearing value of widget + enum ClearMode + { + ClearToNull, //!< Reset value to null + ClearToDefault, //!< Reset value to default value (see defaultValue() ) + }; + + /** Constructor for QgsFilterLineEdit. + * @param parent parent widget + * @param nullValue string for representing null values + */ QgsFilterLineEdit( QWidget* parent /TransferThis/ = 0, const QString& nullValue = QString::null ); + /** Returns true if the widget's clear button is visible. + * @see setShowClearButton() + * @note added in QGIS 3.0 + */ + bool showClearButton() const; + + /** Sets whether the widget's clear button is visible. + * @param visible set to false to hide the clear button + * @see showClearButton() + * @note added in QGIS 3.0 + */ + void setShowClearButton( bool visible ); + + /** Returns the clear mode for the widget. The clear mode defines the behaviour of the + * widget when its value is cleared. This defaults to ClearToNull. + * @see setClearMode() + * @note added in QGIS 3.0 + */ + ClearMode clearMode() const; + + /** Sets the clear mode for the widget. The clear mode defines the behaviour of the + * widget when its value is cleared. This defaults to ClearToNull. + * @see clearMode() + * @note added in QGIS 3.0 + */ + void setClearMode( ClearMode mode ); + + /** Sets the string representation for null values in the widget. This does not + * affect the values returned for null values by value(), rather it only affects + * the text that is shown to users when the widget's value is null. + * @param nullValue string to show when widget's value is null + * @see nullValue() + */ void setNullValue( const QString& nullValue ); + /** Returns the string used for representating null values in the widget. + * @see setNullValue() + * @see isNull() + */ QString nullValue() const; + /** Sets the default value for the widget. The default value is a value + * which the widget will be reset to if it is cleared and the clearMode() + * is equal to ClearToDefault. + * @param defaultValue default value + * @see defaultValue() + * @see clearMode() + * @note added in QGIS 3.0 + */ + void setDefaultValue( const QString& defaultValue ); + + /** Returns the default value for the widget. The default value is a value + * which the widget will be reset to if it is cleared and the clearMode() + * is equal to ClearToDefault. + * @see setDefaultValue() + * @see clearMode() + * @note added in QGIS 3.0 + */ + QString defaultValue() const; + /** - * Sets the current text with NULL support + * Sets the current text for the widget with support for handling null values. * - * @param value The text to set. If a Null string is provided, the text will match the nullValue. + * @param value The text to set. If a null string is provided, the text shown in the + * widget will be set to the current nullValue(). + * @see value() */ void setValue( const QString& value ); /** - * Returns the text of this edit with NULL support + * Returns the text of this edit with support for handling null values. If the text + * in the widget matches the current nullValue() then the returned value will be + * a null string. * - * @return Current text (Null string if it matches the nullValue property ) + * @return Current text (or null string if it matches the nullValue() property ) + * @see setValue() */ QString value() const; /** - * Determine if the current text represents Null. + * Determine if the current text represents null. * - * @return True if the value is Null. + * @return True if the widget's value is null. + * @see nullValue() */ bool isNull() const; + public slots: + + /** Clears the widget and resets it to the null value. + * @see nullValue() + * @note added in QGIS 3.0 + */ + virtual void clearValue(); + signals: + + /** Emitted when the widget is cleared + * @see clearValue() + */ void cleared(); /** - * Same as textChanged(const QString& ) but with support for Null values. + * Same as textChanged() but with support for null values. * - * @param value The current text or Null string if it matches the nullValue property. + * @param value The current text or null string if it matches the nullValue() property. */ void valueChanged( const QString& value ); diff --git a/src/gui/editorwidgets/qgsdoublespinbox.cpp b/src/gui/editorwidgets/qgsdoublespinbox.cpp index 904cf0d7ceb..5325c5aeb26 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.cpp +++ b/src/gui/editorwidgets/qgsdoublespinbox.cpp @@ -17,12 +17,14 @@ #include #include #include -#include #include "qgsdoublespinbox.h" #include "qgsexpression.h" #include "qgsapplication.h" #include "qgslogger.h" +#include "qgsfilterlineedit.h" + +#define CLEAR_ICON_SIZE 16 QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent ) : QDoubleSpinBox( parent ) @@ -31,25 +33,22 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent ) , mCustomClearValue( 0.0 ) , mExpressionsEnabled( true ) { - mClearButton = new QToolButton( this ); - mClearButton->setIcon( QgsApplication::getThemeIcon( "/mIconClear.svg" ) ); - mClearButton->setCursor( Qt::ArrowCursor ); - mClearButton->setStyleSheet( "position: absolute; border: none; padding: 0px;" ); - connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clear() ) ); + mLineEdit = new QgsSpinBoxLineEdit(); - setStyleSheet( QString( "padding-right: %1px;" ).arg( mClearButton->sizeHint().width() + 18 + frameWidth() + 1 ) ); + setLineEdit( mLineEdit ); QSize msz = minimumSizeHint(); - setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ), - qMax( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) ); + setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2, + qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); + connect( mLineEdit, SIGNAL( cleared() ), this, SLOT( clear() ) ); connect( this, SIGNAL( valueChanged( double ) ), this, SLOT( changed( double ) ) ); } void QgsDoubleSpinBox::setShowClearButton( const bool showClearButton ) { mShowClearButton = showClearButton; - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( showClearButton ); } void QgsDoubleSpinBox::setExpressionsEnabled( const bool enabled ) @@ -60,18 +59,18 @@ void QgsDoubleSpinBox::setExpressionsEnabled( const bool enabled ) void QgsDoubleSpinBox::changeEvent( QEvent *event ) { QDoubleSpinBox::changeEvent( event ); - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) ); } void QgsDoubleSpinBox::paintEvent( QPaintEvent *event ) { - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) ); QDoubleSpinBox::paintEvent( event ); } void QgsDoubleSpinBox::changed( double value ) { - mClearButton->setVisible( shouldShowClearForValue( value ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value ) ); } void QgsDoubleSpinBox::clear() @@ -187,14 +186,3 @@ bool QgsDoubleSpinBox::shouldShowClearForValue( const double value ) const } return value != clearValue(); } - -void QgsDoubleSpinBox::resizeEvent( QResizeEvent * event ) -{ - QDoubleSpinBox::resizeEvent( event ); - - QSize sz = mClearButton->sizeHint(); - - mClearButton->move( rect().right() - frameWidth() - 18 - sz.width(), - ( rect().bottom() + 1 - sz.height() ) / 2 ); - -} diff --git a/src/gui/editorwidgets/qgsdoublespinbox.h b/src/gui/editorwidgets/qgsdoublespinbox.h index 55cff69a6ab..6fc55dd27c7 100644 --- a/src/gui/editorwidgets/qgsdoublespinbox.h +++ b/src/gui/editorwidgets/qgsdoublespinbox.h @@ -13,11 +13,12 @@ * * ***************************************************************************/ -#ifndef QGSDOUBLESPPINBOX_H -#define QGSDOUBLESPPINBOX_H +#ifndef QGSDOUBLESPINBOX_H +#define QGSDOUBLESPINBOX_H #include -#include + +class QgsSpinBoxLineEdit; /** \ingroup gui * @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value. @@ -28,21 +29,34 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton ) + Q_PROPERTY( bool clearValue READ clearValue WRITE setClearValue ) Q_PROPERTY( bool expressionsEnabled READ expressionsEnabled WRITE setExpressionsEnabled ) public: + + //! Behaviour when widget is cleared. enum ClearValueMode { - MinimumValue, - MaximumValue, - CustomValue + MinimumValue, //!< Reset value to minimum() + MaximumValue, //!< Reset value to maximum() + CustomValue, //!< Reset value to custom value (see setClearValue() ) }; + /** Constructor for QgsDoubleSpinBox. + * @param parent parent widget + */ explicit QgsDoubleSpinBox( QWidget *parent = nullptr ); - //! determines if the widget will show a clear button - //! @note the clear button will set the widget to its minimum value + /** Sets whether the widget will show a clear button. The clear button + * allows users to reset the widget to a default or empty state. + * @param showClearButton set to true to show the clear button, or false to hide it + * @see showClearButton() + */ void setShowClearButton( const bool showClearButton ); + + /** Returns whether the widget is showing a clear button. + * @see setShowClearButton() + */ bool showClearButton() const {return mShowClearButton;} /** Sets if the widget will allow entry of simple expressions, which are @@ -51,6 +65,7 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox * @note added in QGIS 2.7 */ void setExpressionsEnabled( const bool enabled ); + /** Returns whether the widget will allow entry of simple expressions, which are * evaluated and then discarded. * @returns true if spin box allows expression entry @@ -62,28 +77,30 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox virtual void clear() override; /** - * @brief setClearValue defines the clear value as a custom value and will automatically set the clear value mode to CustomValue + * Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue. * @param customValue defines the numerical value used as the clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. + * @see setClearValue() */ void setClearValue( double customValue, const QString& clearValueText = QString() ); + /** - * @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value + * Defines if the clear value should be the minimum or maximum values of the widget or a custom value. * @param mode mode to user for clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. */ void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() ); - //! returns the value used when clear() is called. + /** Returns the value used when clear() is called. + * @see setClearValue() + */ double clearValue() const; virtual double valueFromText( const QString & text ) const override; virtual QValidator::State validate( QString & input, int & pos ) const override; - void paintEvent( QPaintEvent* e ) override; protected: - virtual void resizeEvent( QResizeEvent* event ) override; virtual void changeEvent( QEvent* event ) override; private slots: @@ -93,7 +110,7 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox int frameWidth() const; bool shouldShowClearForValue( const double value ) const; - void updateStyleSheet( const QColor& backgroundColor = QColor() ); + QgsSpinBoxLineEdit* mLineEdit; bool mShowClearButton; ClearValueMode mClearValueMode; @@ -101,8 +118,7 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox bool mExpressionsEnabled; - QToolButton* mClearButton; QString stripped( const QString &originalText ) const; }; -#endif // QGSDOUBLESPPINBOX_H +#endif // QGSDOUBLESPINBOX_H diff --git a/src/gui/editorwidgets/qgsspinbox.cpp b/src/gui/editorwidgets/qgsspinbox.cpp index c3f3f0c907e..5373b14f12b 100644 --- a/src/gui/editorwidgets/qgsspinbox.cpp +++ b/src/gui/editorwidgets/qgsspinbox.cpp @@ -17,12 +17,14 @@ #include #include #include -#include #include "qgsspinbox.h" #include "qgsexpression.h" #include "qgsapplication.h" #include "qgslogger.h" +#include "qgsfilterlineedit.h" + +#define CLEAR_ICON_SIZE 16 QgsSpinBox::QgsSpinBox( QWidget *parent ) : QSpinBox( parent ) @@ -31,25 +33,22 @@ QgsSpinBox::QgsSpinBox( QWidget *parent ) , mCustomClearValue( 0 ) , mExpressionsEnabled( true ) { - mClearButton = new QToolButton( this ); - mClearButton->setIcon( QgsApplication::getThemeIcon( "/mIconClear.svg" ) ); - mClearButton->setCursor( Qt::ArrowCursor ); - mClearButton->setStyleSheet( "position: absolute; border: none; padding: 0px;" ); - connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clear() ) ); + mLineEdit = new QgsSpinBoxLineEdit(); - setStyleSheet( QString( "padding-right: %1px;" ).arg( mClearButton->sizeHint().width() + 18 + frameWidth() + 1 ) ); + setLineEdit( mLineEdit ); QSize msz = minimumSizeHint(); - setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ), - qMax( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) ); + setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2, + qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) ); + connect( mLineEdit, SIGNAL( cleared() ), this, SLOT( clear() ) ); connect( this, SIGNAL( valueChanged( int ) ), this, SLOT( changed( int ) ) ); } void QgsSpinBox::setShowClearButton( const bool showClearButton ) { mShowClearButton = showClearButton; - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( showClearButton ); } void QgsSpinBox::setExpressionsEnabled( const bool enabled ) @@ -60,18 +59,18 @@ void QgsSpinBox::setExpressionsEnabled( const bool enabled ) void QgsSpinBox::changeEvent( QEvent *event ) { QSpinBox::changeEvent( event ); - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) ); } void QgsSpinBox::paintEvent( QPaintEvent *event ) { - mClearButton->setVisible( shouldShowClearForValue( value() ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) ); QSpinBox::paintEvent( event ); } void QgsSpinBox::changed( int value ) { - mClearButton->setVisible( shouldShowClearForValue( value ) ); + mLineEdit->setShowClearButton( shouldShowClearForValue( value ) ); } void QgsSpinBox::clear() @@ -187,14 +186,3 @@ QString QgsSpinBox::stripped( const QString &originalText ) const return text; } - -void QgsSpinBox::resizeEvent( QResizeEvent * event ) -{ - QSpinBox::resizeEvent( event ); - - QSize sz = mClearButton->sizeHint(); - - mClearButton->move( rect().right() - frameWidth() - 18 - sz.width(), - ( rect().bottom() + 1 - sz.height() ) / 2 ); - -} diff --git a/src/gui/editorwidgets/qgsspinbox.h b/src/gui/editorwidgets/qgsspinbox.h index 38400cc5963..d0bff10bd32 100644 --- a/src/gui/editorwidgets/qgsspinbox.h +++ b/src/gui/editorwidgets/qgsspinbox.h @@ -13,11 +13,12 @@ * * ***************************************************************************/ -#ifndef QGSSPPINBOX_H -#define QGSSPPINBOX_H +#ifndef QGSSPINBOX_H +#define QGSSPINBOX_H #include -#include + +class QgsSpinBoxLineEdit; /** \ingroup gui * @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value. @@ -28,20 +29,34 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox { Q_OBJECT Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton ) + Q_PROPERTY( bool clearValue READ clearValue WRITE setClearValue ) + Q_PROPERTY( bool expressionsEnabled READ expressionsEnabled WRITE setExpressionsEnabled ) public: + + //! Behaviour when widget is cleared. enum ClearValueMode { - MinimumValue, - MaximumValue, - CustomValue + MinimumValue, //!< Reset value to minimum() + MaximumValue, //!< Reset value to maximum() + CustomValue, //!< Reset value to custom value (see setClearValue() ) }; + /** Constructor for QgsSpinBox. + * @param parent parent widget + */ explicit QgsSpinBox( QWidget *parent = nullptr ); - //! determines if the widget will show a clear button - //! @note the clear button will set the widget to its minimum value + /** Sets whether the widget will show a clear button. The clear button + * allows users to reset the widget to a default or empty state. + * @param showClearButton set to true to show the clear button, or false to hide it + * @see showClearButton() + */ void setShowClearButton( const bool showClearButton ); + + /** Returns whether the widget is showing a clear button. + * @see setShowClearButton() + */ bool showClearButton() const {return mShowClearButton;} /** Sets if the widget will allow entry of simple expressions, which are @@ -50,6 +65,7 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox * @note added in QGIS 2.7 */ void setExpressionsEnabled( const bool enabled ); + /** Returns whether the widget will allow entry of simple expressions, which are * evaluated and then discarded. * @returns true if spin box allows expression entry @@ -61,26 +77,30 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox virtual void clear() override; /** - * @brief setClearValue defines the clear value for the widget and will automatically set the clear value mode to CustomValue + * Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue. * @param customValue defines the numerical value used as the clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. + * @see setClearValue() */ void setClearValue( int customValue, const QString& clearValueText = QString() ); + /** - * @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value + * Defines if the clear value should be the minimum or maximum values of the widget or a custom value. * @param mode mode to user for clear value * @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used. */ void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() ); - //! returns the value used when clear() is called. + /** Returns the value used when clear() is called. + * @see setClearValue() + */ int clearValue() const; virtual int valueFromText( const QString & text ) const override; virtual QValidator::State validate( QString & input, int & pos ) const override; protected: - virtual void resizeEvent( QResizeEvent* event ) override; + virtual void changeEvent( QEvent* event ) override; virtual void paintEvent( QPaintEvent* event ) override; @@ -91,14 +111,15 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox int frameWidth() const; bool shouldShowClearForValue( const int value ) const; + QgsSpinBoxLineEdit* mLineEdit; + bool mShowClearButton; ClearValueMode mClearValueMode; int mCustomClearValue; bool mExpressionsEnabled; - QToolButton* mClearButton; QString stripped( const QString &originalText ) const; }; -#endif // QGSSPPINBOX_H +#endif // QGSSPINBOX_H diff --git a/src/gui/qgsfilterlineedit.cpp b/src/gui/qgsfilterlineedit.cpp index dcb97424db6..99394dd9116 100644 --- a/src/gui/qgsfilterlineedit.cpp +++ b/src/gui/qgsfilterlineedit.cpp @@ -25,6 +25,8 @@ QgsFilterLineEdit::QgsFilterLineEdit( QWidget* parent, const QString& nullValue ) : QLineEdit( parent ) + , mClearButtonVisible( true ) + , mClearMode( ClearToNull ) , mNullValue( nullValue ) , mFocusInEvent( false ) , mClearHover( false ) @@ -40,11 +42,15 @@ QgsFilterLineEdit::QgsFilterLineEdit( QWidget* parent, const QString& nullValue connect( this, SIGNAL( textChanged( const QString& ) ), this, SLOT( onTextChanged( const QString& ) ) ); +} - int frameWidth = style()->pixelMetric( QStyle::PM_DefaultFrameWidth ); - QSize msz = minimumSizeHint(); - setMinimumSize( qMax( msz.width(), mClearIconSize.width() + frameWidth * 2 + 2 ), - qMax( msz.height(), mClearIconSize.height() + frameWidth * 2 + 2 ) ); +void QgsFilterLineEdit::setShowClearButton( bool visible ) +{ + mClearButtonVisible = visible; + if ( !visible ) + mClearHover = false; + + update(); } void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e ) @@ -56,8 +62,7 @@ void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e ) if ( shouldShowClear() && clearRect().contains( e->pos() ) ) { - clear(); - emit cleared(); + clearValue(); } } @@ -91,10 +96,27 @@ void QgsFilterLineEdit::focusInEvent( QFocusEvent* e ) } } -void QgsFilterLineEdit::clear() +void QgsFilterLineEdit::clearValue() { - setText( mNullValue ); + switch ( mClearMode ) + { + case ClearToNull: + setText( mNullValue ); + break; + + case ClearToDefault: + setText( mDefaultValue ); + break; + } + + if ( mClearHover ) + { + setCursor( Qt::IBeamCursor ); + mClearHover = false; + } + setModified( true ); + emit cleared(); } void QgsFilterLineEdit::paintEvent( QPaintEvent* e ) @@ -134,11 +156,28 @@ void QgsFilterLineEdit::onTextChanged( const QString &text ) setStyleSheet( mStyleSheet ); emit valueChanged( text ); } + + if ( mClearHover && !shouldShowClear() ) + { + setCursor( Qt::IBeamCursor ); + mClearHover = false; + } } bool QgsFilterLineEdit::shouldShowClear() const { - return isEnabled() && !isReadOnly() && !isNull(); + if ( !isEnabled() || isReadOnly() || !mClearButtonVisible ) + return false; + + switch ( mClearMode ) + { + case ClearToNull: + return !isNull(); + + case ClearToDefault: + return value() != mDefaultValue; + } + return false; //avoid warnings } QRect QgsFilterLineEdit::clearRect() const diff --git a/src/gui/qgsfilterlineedit.h b/src/gui/qgsfilterlineedit.h index 762d6c1ca80..9c9c8e89e12 100644 --- a/src/gui/qgsfilterlineedit.h +++ b/src/gui/qgsfilterlineedit.h @@ -22,49 +22,146 @@ class QToolButton; -/** \ingroup gui - * Lineedit with builtin clear button +/** \class QgsFilterLineEdit + * \ingroup gui + * QLineEdit subclass with built in support for clearing the widget's value and + * handling custom null value representations. + * + * When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used + * instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged() + * signal should be used instead of textChanged(). **/ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit { Q_OBJECT + Q_ENUMS( ClearMode ) + Q_PROPERTY( ClearMode clearMode READ clearMode WRITE setClearMode ) Q_PROPERTY( QString nullValue READ nullValue WRITE setNullValue ) + Q_PROPERTY( QString defaultValue READ defaultValue WRITE setDefaultValue ) + Q_PROPERTY( QString value READ value WRITE setValue NOTIFY valueChanged ) + Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton ) public: + + //! Behaviour when clearing value of widget + enum ClearMode + { + ClearToNull = 0, //!< Reset value to null + ClearToDefault, //!< Reset value to default value (see defaultValue() ) + }; + + /** Constructor for QgsFilterLineEdit. + * @param parent parent widget + * @param nullValue string for representing null values + */ QgsFilterLineEdit( QWidget* parent = nullptr, const QString& nullValue = QString::null ); + /** Returns true if the widget's clear button is visible. + * @see setShowClearButton() + * @note added in QGIS 3.0 + */ + bool showClearButton() const { return mClearButtonVisible; } + + /** Sets whether the widget's clear button is visible. + * @param visible set to false to hide the clear button + * @see showClearButton() + * @note added in QGIS 3.0 + */ + void setShowClearButton( bool visible ); + + /** Returns the clear mode for the widget. The clear mode defines the behaviour of the + * widget when its value is cleared. This defaults to ClearToNull. + * @see setClearMode() + * @note added in QGIS 3.0 + */ + ClearMode clearMode() const { return mClearMode; } + + /** Sets the clear mode for the widget. The clear mode defines the behaviour of the + * widget when its value is cleared. This defaults to ClearToNull. + * @see clearMode() + * @note added in QGIS 3.0 + */ + void setClearMode( ClearMode mode ) { mClearMode = mode; } + + /** Sets the string representation for null values in the widget. This does not + * affect the values returned for null values by value(), rather it only affects + * the text that is shown to users when the widget's value is null. + * @param nullValue string to show when widget's value is null + * @see nullValue() + */ void setNullValue( const QString& nullValue ) { mNullValue = nullValue; } + /** Returns the string used for representating null values in the widget. + * @see setNullValue() + * @see isNull() + */ QString nullValue() const { return mNullValue; } + /** Sets the default value for the widget. The default value is a value + * which the widget will be reset to if it is cleared and the clearMode() + * is equal to ClearToDefault. + * @param defaultValue default value + * @see defaultValue() + * @see clearMode() + * @note added in QGIS 3.0 + */ + void setDefaultValue( const QString& defaultValue ) { mDefaultValue = defaultValue; } + + /** Returns the default value for the widget. The default value is a value + * which the widget will be reset to if it is cleared and the clearMode() + * is equal to ClearToDefault. + * @see setDefaultValue() + * @see clearMode() + * @note added in QGIS 3.0 + */ + QString defaultValue() const { return mDefaultValue; } + /** - * Sets the current text with NULL support + * Sets the current text for the widget with support for handling null values. * - * @param value The text to set. If a Null string is provided, the text will match the nullValue. + * @param value The text to set. If a null string is provided, the text shown in the + * widget will be set to the current nullValue(). + * @see value() */ void setValue( const QString& value ) { setText( value.isNull() ? mNullValue : value ); } /** - * Returns the text of this edit with NULL support + * Returns the text of this edit with support for handling null values. If the text + * in the widget matches the current nullValue() then the returned value will be + * a null string. * - * @return Current text (Null string if it matches the nullValue property ) + * @return Current text (or null string if it matches the nullValue() property ) + * @see setValue() */ QString value() const { return isNull() ? QString::null : text(); } /** - * Determine if the current text represents Null. + * Determine if the current text represents null. * - * @return True if the value is Null. + * @return True if the widget's value is null. + * @see nullValue() */ inline bool isNull() const { return text() == mNullValue; } + public slots: + + /** Clears the widget and resets it to the null value. + * @see nullValue() + * @note added in QGIS 3.0 + */ + virtual void clearValue(); + signals: + + /** Emitted when the widget is cleared + * @see clearValue() + */ void cleared(); /** - * Same as textChanged(const QString& ) but with support for Null values. + * Same as textChanged() but with support for null values. * - * @param value The current text or Null string if it matches the nullValue property. + * @param value The current text or null string if it matches the nullValue() property. */ void valueChanged( const QString& value ); @@ -76,11 +173,16 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit void leaveEvent( QEvent* e ) override; private slots: - void clear(); void onTextChanged( const QString &text ); private: + + bool mClearButtonVisible; + + ClearMode mClearMode; + QString mNullValue; + QString mDefaultValue; QString mStyleSheet; bool mFocusInEvent; bool mClearHover; @@ -95,4 +197,33 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit QRect clearRect() const; }; +/// @cond PRIVATE + +/** Private QgsFilterLineEdit subclass for use as a line edit in QgsSpinBox/QgsDoubleSpinBox + * we let QgsFilterLineEdit handle display of the clear button and detection + * of clicks, but override clearValue() and let Qgs(Double)SpinBox handle the clearing + * themselves. + */ +class QgsSpinBoxLineEdit : public QgsFilterLineEdit +{ + Q_OBJECT + + public: + + QgsSpinBoxLineEdit( QWidget* parent = nullptr ) + : QgsFilterLineEdit( parent ) + {} + + public slots: + + virtual void clearValue() override + { + // don't change the value - let spin boxes handle that by detecting cleared() signal + setCursor( Qt::IBeamCursor ); + setModified( true ); + emit cleared(); + } +}; +/// @endcond + #endif // QGSFILTERLINEEDIT_H diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 7ab831c25f5..3d750bd6909 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -46,6 +46,7 @@ ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py) ADD_PYTHON_TEST(PyQgsProject test_qgsproject.py) ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py) ADD_PYTHON_TEST(PyQgsField test_qgsfield.py) +ADD_PYTHON_TEST(PyQgsFilterLineEdit test_qgsfilterlineedit.py) ADD_PYTHON_TEST(PyQgsFontUtils test_qgsfontutils.py) ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersections.py) ADD_PYTHON_TEST(PyQgsGeometryGeneratorSymbolLayer test_qgsgeometrygeneratorsymbollayer.py) diff --git a/tests/src/python/test_qgsfilterlineedit.py b/tests/src/python/test_qgsfilterlineedit.py new file mode 100644 index 00000000000..f530c3f6c06 --- /dev/null +++ b/tests/src/python/test_qgsfilterlineedit.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsFilterLineEdit + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '20/08/2016' +__copyright__ = 'Copyright 2016, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.gui import QgsFilterLineEdit + +try: + from qgis.PyQt.QtTest import QSignalSpy + use_signal_spy = True +except: + use_signal_spy = False + +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsFilterLineEdit(unittest.TestCase): + + def testGettersSetters(self): + """ test widget getters/setters """ + w = qgis.gui.QgsFilterLineEdit() + + w.setNullValue('null') + self.assertEqual(w.nullValue(), 'null') + w.setValue('value') + self.assertEqual(w.value(), 'value') + self.assertEqual(w.text(), 'value') + w.setDefaultValue('default') + self.assertEqual(w.defaultValue(), 'default') + w.setClearMode(QgsFilterLineEdit.ClearToDefault) + self.assertEqual(w.clearMode(), QgsFilterLineEdit.ClearToDefault) + w.setShowClearButton(False) + self.assertFalse(w.showClearButton()) + w.setShowClearButton(True) + self.assertTrue(w.showClearButton()) + + def testNullValueHandling(self): + """ test widget handling of null values """ + w = qgis.gui.QgsFilterLineEdit() + + # start with no null value + w.setValue(None) + self.assertTrue(w.isNull()) + w.setValue('a') + self.assertEqual(w.text(), 'a') + self.assertFalse(w.isNull()) + + # set a null value + w.setNullValue('null') + self.assertEqual(w.value(), 'a') + self.assertEqual(w.text(), 'a') + self.assertFalse(w.isNull()) + + w.setValue(None) + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + self.assertEqual(w.text(), 'null') + + w.setValue('null') + self.assertEqual(w.text(), 'null') + # ND: I don't think this following logic is correct - should be a distinction between + # the widget's representation of null and the actual value. Ie isNull() + # should be false and value() should return 'null' + # in other words - if you break this test to match my desired behaviour, feel free to remove it! + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + def testClearToNull(self): + """ test clearing widget """ + w = qgis.gui.QgsFilterLineEdit() + + w.setValue('abc') + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + w.setNullValue('def') + w.setValue('abc') + self.assertFalse(w.isNull()) + w.clearValue() + self.assertEqual(w.text(), 'def') + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + def testClearToDefault(self): + # test clearing to default value + w = qgis.gui.QgsFilterLineEdit() + w.setClearMode(QgsFilterLineEdit.ClearToDefault) + + w.setValue('abc') + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + w.setDefaultValue('def') + w.setValue('abc') + self.assertFalse(w.isNull()) + w.clearValue() + self.assertEqual(w.value(), 'def') + self.assertEqual(w.text(), 'def') + self.assertFalse(w.isNull()) + + @unittest.skipIf(not use_signal_spy, "No QSignalSpy available") + def test_ChangedSignals(self): + """ test that signals are correctly emitted when clearing""" + + w = qgis.gui.QgsFilterLineEdit() + + cleared_spy = QSignalSpy(w.cleared) + w.setValue('1') + w.clearValue() + + self.assertEqual(len(cleared_spy), 1) + + +if __name__ == '__main__': + unittest.main()