Merge pull request #3468 from nyalldawson/spin_box_v2

Rework Qgs(Double)SpinBox clear handling
This commit is contained in:
Nyall Dawson 2016-09-09 07:02:28 +10:00 committed by GitHub
commit f52a9a00d8
11 changed files with 584 additions and 127 deletions

View File

@ -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 );
};

View File

@ -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 );
};

View File

@ -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 <qgsfilterlineedit.h>
%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 );

View File

@ -17,12 +17,14 @@
#include <QMouseEvent>
#include <QSettings>
#include <QStyle>
#include <QToolButton>
#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 );
}

View File

@ -13,11 +13,12 @@
* *
***************************************************************************/
#ifndef QGSDOUBLESPPINBOX_H
#define QGSDOUBLESPPINBOX_H
#ifndef QGSDOUBLESPINBOX_H
#define QGSDOUBLESPINBOX_H
#include <QDoubleSpinBox>
#include <QToolButton>
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

View File

@ -17,12 +17,14 @@
#include <QMouseEvent>
#include <QSettings>
#include <QStyle>
#include <QToolButton>
#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 );
}

View File

@ -13,11 +13,12 @@
* *
***************************************************************************/
#ifndef QGSSPPINBOX_H
#define QGSSPPINBOX_H
#ifndef QGSSPINBOX_H
#define QGSSPINBOX_H
#include <QSpinBox>
#include <QToolButton>
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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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()