Skip to content

Commit f52a9a0

Browse files
authoredSep 8, 2016
Merge pull request #3468 from nyalldawson/spin_box_v2
Rework Qgs(Double)SpinBox clear handling
2 parents a90217d + f16b387 commit f52a9a0

File tree

11 files changed

+584
-127
lines changed

11 files changed

+584
-127
lines changed
 
Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,40 @@
1+
/** \ingroup gui
2+
* @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
3+
* The clear value can be either the minimum or the maiximum value of the spin box or a custom value.
4+
* This value can then be handled by a special value text.
5+
*/
6+
17
class QgsDoubleSpinBox : QDoubleSpinBox
28
{
39
%TypeHeaderCode
410
#include <qgsdoublespinbox.h>
511
%End
612

713
public:
14+
15+
//! Behaviour when widget is cleared.
816
enum ClearValueMode
917
{
10-
MinimumValue,
11-
MaximumValue,
12-
CustomValue
18+
MinimumValue, //!< Reset value to minimum()
19+
MaximumValue, //!< Reset value to maximum()
20+
CustomValue, //!< Reset value to custom value (see setClearValue() )
1321
};
1422

23+
/** Constructor for QgsDoubleSpinBox.
24+
* @param parent parent widget
25+
*/
1526
explicit QgsDoubleSpinBox( QWidget *parent /TransferThis/ = 0 );
1627

17-
//! determines if the widget will show a clear button
18-
//! @note the clear button will set the widget to its minimum value
28+
/** Sets whether the widget will show a clear button. The clear button
29+
* allows users to reset the widget to a default or empty state.
30+
* @param showClearButton set to true to show the clear button, or false to hide it
31+
* @see showClearButton()
32+
*/
1933
void setShowClearButton( const bool showClearButton );
34+
35+
/** Returns whether the widget is showing a clear button.
36+
* @see setShowClearButton()
37+
*/
2038
bool showClearButton() const;
2139

2240
/** Sets if the widget will allow entry of simple expressions, which are
@@ -25,6 +43,7 @@ class QgsDoubleSpinBox : QDoubleSpinBox
2543
* @note added in QGIS 2.7
2644
*/
2745
void setExpressionsEnabled( const bool enabled );
46+
2847
/** Returns whether the widget will allow entry of simple expressions, which are
2948
* evaluated and then discarded.
3049
* @returns true if spin box allows expression entry
@@ -36,26 +55,29 @@ class QgsDoubleSpinBox : QDoubleSpinBox
3655
virtual void clear();
3756

3857
/**
39-
* @brief setClearValue defines the clear value as a custom value and will automatically set the clear value mode to CustomValue
58+
* Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue.
4059
* @param customValue defines the numerical value used as the clear value
4160
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
61+
* @see setClearValue()
4262
*/
4363
void setClearValue( double customValue, const QString& clearValueText = QString() );
64+
4465
/**
45-
* @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value
66+
* Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
4667
* @param mode mode to user for clear value
4768
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
4869
*/
4970
void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() );
5071

51-
//! returns the value used when clear() is called.
72+
/** Returns the value used when clear() is called.
73+
* @see setClearValue()
74+
*/
5275
double clearValue() const;
5376

5477
virtual double valueFromText( const QString & text ) const;
5578
virtual QValidator::State validate( QString & input, int & pos ) const;
5679

5780
protected:
58-
virtual void resizeEvent( QResizeEvent* event );
5981
virtual void changeEvent( QEvent* event );
6082
virtual void paintEvent( QPaintEvent* event );
6183
};
Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,40 @@
1+
/** \ingroup gui
2+
* @brief The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
3+
* The clear value can be either the minimum or the maiximum value of the spin box or a custom value.
4+
* This value can then be handled by a special value text.
5+
*/
6+
17
class QgsSpinBox : QSpinBox
28
{
39
%TypeHeaderCode
410
#include <qgsspinbox.h>
511
%End
612

713
public:
14+
15+
//! Behaviour when widget is cleared.
816
enum ClearValueMode
917
{
10-
MinimumValue,
11-
MaximumValue,
12-
CustomValue
18+
MinimumValue, //!< Reset value to minimum()
19+
MaximumValue, //!< Reset value to maximum()
20+
CustomValue, //!< Reset value to custom value (see setClearValue() )
1321
};
1422

23+
/** Constructor for QgsSpinBox.
24+
* @param parent parent widget
25+
*/
1526
explicit QgsSpinBox( QWidget *parent /TransferThis/ = 0 );
1627

17-
//! determines if the widget will show a clear button
18-
//! @note the clear button will set the widget to its minimum value
28+
/** Sets whether the widget will show a clear button. The clear button
29+
* allows users to reset the widget to a default or empty state.
30+
* @param showClearButton set to true to show the clear button, or false to hide it
31+
* @see showClearButton()
32+
*/
1933
void setShowClearButton( const bool showClearButton );
34+
35+
/** Returns whether the widget is showing a clear button.
36+
* @see setShowClearButton()
37+
*/
2038
bool showClearButton() const;
2139

2240
/** Sets if the widget will allow entry of simple expressions, which are
@@ -25,6 +43,7 @@ class QgsSpinBox : QSpinBox
2543
* @note added in QGIS 2.7
2644
*/
2745
void setExpressionsEnabled( const bool enabled );
46+
2847
/** Returns whether the widget will allow entry of simple expressions, which are
2948
* evaluated and then discarded.
3049
* @returns true if spin box allows expression entry
@@ -36,26 +55,30 @@ class QgsSpinBox : QSpinBox
3655
virtual void clear();
3756

3857
/**
39-
* @brief setClearValue defines the clear value for the widget and will automatically set the clear value mode to CustomValue
58+
* Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue.
4059
* @param customValue defines the numerical value used as the clear value
4160
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
61+
* @see setClearValue()
4262
*/
4363
void setClearValue( int customValue, const QString& clearValueText = QString() );
64+
4465
/**
45-
* @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value
66+
* Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
4667
* @param mode mode to user for clear value
4768
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
4869
*/
4970
void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() );
5071

51-
//! returns the value used when clear() is called.
72+
/** Returns the value used when clear() is called.
73+
* @see setClearValue()
74+
*/
5275
int clearValue() const;
5376

5477
virtual int valueFromText( const QString & text ) const;
5578
virtual QValidator::State validate( QString & input, int & pos ) const;
5679

5780
protected:
58-
virtual void resizeEvent( QResizeEvent* event );
81+
5982
virtual void changeEvent( QEvent* event );
6083
virtual void paintEvent( QPaintEvent* event );
6184
};

‎python/gui/qgsfilterlineedit.sip

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,138 @@
1-
2-
/** LineEdit with builtin clear button
3-
*/
1+
/** \class QgsFilterLineEdit
2+
* \ingroup gui
3+
* QLineEdit subclass with built in support for clearing the widget's value and
4+
* handling custom null value representations.
5+
*
6+
* When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used
7+
* instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged()
8+
* signal should be used instead of textChanged().
9+
**/
410
class QgsFilterLineEdit : QLineEdit
511
{
612
%TypeHeaderCode
713
#include <qgsfilterlineedit.h>
814
%End
915
public:
16+
17+
//! Behaviour when clearing value of widget
18+
enum ClearMode
19+
{
20+
ClearToNull, //!< Reset value to null
21+
ClearToDefault, //!< Reset value to default value (see defaultValue() )
22+
};
23+
24+
/** Constructor for QgsFilterLineEdit.
25+
* @param parent parent widget
26+
* @param nullValue string for representing null values
27+
*/
1028
QgsFilterLineEdit( QWidget* parent /TransferThis/ = 0, const QString& nullValue = QString::null );
1129

30+
/** Returns true if the widget's clear button is visible.
31+
* @see setShowClearButton()
32+
* @note added in QGIS 3.0
33+
*/
34+
bool showClearButton() const;
35+
36+
/** Sets whether the widget's clear button is visible.
37+
* @param visible set to false to hide the clear button
38+
* @see showClearButton()
39+
* @note added in QGIS 3.0
40+
*/
41+
void setShowClearButton( bool visible );
42+
43+
/** Returns the clear mode for the widget. The clear mode defines the behaviour of the
44+
* widget when its value is cleared. This defaults to ClearToNull.
45+
* @see setClearMode()
46+
* @note added in QGIS 3.0
47+
*/
48+
ClearMode clearMode() const;
49+
50+
/** Sets the clear mode for the widget. The clear mode defines the behaviour of the
51+
* widget when its value is cleared. This defaults to ClearToNull.
52+
* @see clearMode()
53+
* @note added in QGIS 3.0
54+
*/
55+
void setClearMode( ClearMode mode );
56+
57+
/** Sets the string representation for null values in the widget. This does not
58+
* affect the values returned for null values by value(), rather it only affects
59+
* the text that is shown to users when the widget's value is null.
60+
* @param nullValue string to show when widget's value is null
61+
* @see nullValue()
62+
*/
1263
void setNullValue( const QString& nullValue );
1364

65+
/** Returns the string used for representating null values in the widget.
66+
* @see setNullValue()
67+
* @see isNull()
68+
*/
1469
QString nullValue() const;
1570

71+
/** Sets the default value for the widget. The default value is a value
72+
* which the widget will be reset to if it is cleared and the clearMode()
73+
* is equal to ClearToDefault.
74+
* @param defaultValue default value
75+
* @see defaultValue()
76+
* @see clearMode()
77+
* @note added in QGIS 3.0
78+
*/
79+
void setDefaultValue( const QString& defaultValue );
80+
81+
/** Returns the default value for the widget. The default value is a value
82+
* which the widget will be reset to if it is cleared and the clearMode()
83+
* is equal to ClearToDefault.
84+
* @see setDefaultValue()
85+
* @see clearMode()
86+
* @note added in QGIS 3.0
87+
*/
88+
QString defaultValue() const;
89+
1690
/**
17-
* Sets the current text with NULL support
91+
* Sets the current text for the widget with support for handling null values.
1892
*
19-
* @param value The text to set. If a Null string is provided, the text will match the nullValue.
93+
* @param value The text to set. If a null string is provided, the text shown in the
94+
* widget will be set to the current nullValue().
95+
* @see value()
2096
*/
2197
void setValue( const QString& value );
2298

2399
/**
24-
* Returns the text of this edit with NULL support
100+
* Returns the text of this edit with support for handling null values. If the text
101+
* in the widget matches the current nullValue() then the returned value will be
102+
* a null string.
25103
*
26-
* @return Current text (Null string if it matches the nullValue property )
104+
* @return Current text (or null string if it matches the nullValue() property )
105+
* @see setValue()
27106
*/
28107
QString value() const;
29108

30109
/**
31-
* Determine if the current text represents Null.
110+
* Determine if the current text represents null.
32111
*
33-
* @return True if the value is Null.
112+
* @return True if the widget's value is null.
113+
* @see nullValue()
34114
*/
35115
bool isNull() const;
36116

117+
public slots:
118+
119+
/** Clears the widget and resets it to the null value.
120+
* @see nullValue()
121+
* @note added in QGIS 3.0
122+
*/
123+
virtual void clearValue();
124+
37125
signals:
126+
127+
/** Emitted when the widget is cleared
128+
* @see clearValue()
129+
*/
38130
void cleared();
39131

40132
/**
41-
* Same as textChanged(const QString& ) but with support for Null values.
133+
* Same as textChanged() but with support for null values.
42134
*
43-
* @param value The current text or Null string if it matches the nullValue property.
135+
* @param value The current text or null string if it matches the nullValue() property.
44136
*/
45137
void valueChanged( const QString& value );
46138

‎src/gui/editorwidgets/qgsdoublespinbox.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
#include <QMouseEvent>
1818
#include <QSettings>
1919
#include <QStyle>
20-
#include <QToolButton>
2120

2221
#include "qgsdoublespinbox.h"
2322
#include "qgsexpression.h"
2423
#include "qgsapplication.h"
2524
#include "qgslogger.h"
25+
#include "qgsfilterlineedit.h"
26+
27+
#define CLEAR_ICON_SIZE 16
2628

2729
QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent )
2830
: QDoubleSpinBox( parent )
@@ -31,25 +33,22 @@ QgsDoubleSpinBox::QgsDoubleSpinBox( QWidget *parent )
3133
, mCustomClearValue( 0.0 )
3234
, mExpressionsEnabled( true )
3335
{
34-
mClearButton = new QToolButton( this );
35-
mClearButton->setIcon( QgsApplication::getThemeIcon( "/mIconClear.svg" ) );
36-
mClearButton->setCursor( Qt::ArrowCursor );
37-
mClearButton->setStyleSheet( "position: absolute; border: none; padding: 0px;" );
38-
connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clear() ) );
36+
mLineEdit = new QgsSpinBoxLineEdit();
3937

40-
setStyleSheet( QString( "padding-right: %1px;" ).arg( mClearButton->sizeHint().width() + 18 + frameWidth() + 1 ) );
38+
setLineEdit( mLineEdit );
4139

4240
QSize msz = minimumSizeHint();
43-
setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ),
44-
qMax( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) );
41+
setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2,
42+
qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
4543

44+
connect( mLineEdit, SIGNAL( cleared() ), this, SLOT( clear() ) );
4645
connect( this, SIGNAL( valueChanged( double ) ), this, SLOT( changed( double ) ) );
4746
}
4847

4948
void QgsDoubleSpinBox::setShowClearButton( const bool showClearButton )
5049
{
5150
mShowClearButton = showClearButton;
52-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
51+
mLineEdit->setShowClearButton( showClearButton );
5352
}
5453

5554
void QgsDoubleSpinBox::setExpressionsEnabled( const bool enabled )
@@ -60,18 +59,18 @@ void QgsDoubleSpinBox::setExpressionsEnabled( const bool enabled )
6059
void QgsDoubleSpinBox::changeEvent( QEvent *event )
6160
{
6261
QDoubleSpinBox::changeEvent( event );
63-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
62+
mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
6463
}
6564

6665
void QgsDoubleSpinBox::paintEvent( QPaintEvent *event )
6766
{
68-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
67+
mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
6968
QDoubleSpinBox::paintEvent( event );
7069
}
7170

7271
void QgsDoubleSpinBox::changed( double value )
7372
{
74-
mClearButton->setVisible( shouldShowClearForValue( value ) );
73+
mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
7574
}
7675

7776
void QgsDoubleSpinBox::clear()
@@ -187,14 +186,3 @@ bool QgsDoubleSpinBox::shouldShowClearForValue( const double value ) const
187186
}
188187
return value != clearValue();
189188
}
190-
191-
void QgsDoubleSpinBox::resizeEvent( QResizeEvent * event )
192-
{
193-
QDoubleSpinBox::resizeEvent( event );
194-
195-
QSize sz = mClearButton->sizeHint();
196-
197-
mClearButton->move( rect().right() - frameWidth() - 18 - sz.width(),
198-
( rect().bottom() + 1 - sz.height() ) / 2 );
199-
200-
}

‎src/gui/editorwidgets/qgsdoublespinbox.h

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
* *
1414
***************************************************************************/
1515

16-
#ifndef QGSDOUBLESPPINBOX_H
17-
#define QGSDOUBLESPPINBOX_H
16+
#ifndef QGSDOUBLESPINBOX_H
17+
#define QGSDOUBLESPINBOX_H
1818

1919
#include <QDoubleSpinBox>
20-
#include <QToolButton>
20+
21+
class QgsSpinBoxLineEdit;
2122

2223
/** \ingroup gui
2324
* @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
2829
{
2930
Q_OBJECT
3031
Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton )
32+
Q_PROPERTY( bool clearValue READ clearValue WRITE setClearValue )
3133
Q_PROPERTY( bool expressionsEnabled READ expressionsEnabled WRITE setExpressionsEnabled )
3234

3335
public:
36+
37+
//! Behaviour when widget is cleared.
3438
enum ClearValueMode
3539
{
36-
MinimumValue,
37-
MaximumValue,
38-
CustomValue
40+
MinimumValue, //!< Reset value to minimum()
41+
MaximumValue, //!< Reset value to maximum()
42+
CustomValue, //!< Reset value to custom value (see setClearValue() )
3943
};
4044

45+
/** Constructor for QgsDoubleSpinBox.
46+
* @param parent parent widget
47+
*/
4148
explicit QgsDoubleSpinBox( QWidget *parent = nullptr );
4249

43-
//! determines if the widget will show a clear button
44-
//! @note the clear button will set the widget to its minimum value
50+
/** Sets whether the widget will show a clear button. The clear button
51+
* allows users to reset the widget to a default or empty state.
52+
* @param showClearButton set to true to show the clear button, or false to hide it
53+
* @see showClearButton()
54+
*/
4555
void setShowClearButton( const bool showClearButton );
56+
57+
/** Returns whether the widget is showing a clear button.
58+
* @see setShowClearButton()
59+
*/
4660
bool showClearButton() const {return mShowClearButton;}
4761

4862
/** Sets if the widget will allow entry of simple expressions, which are
@@ -51,6 +65,7 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
5165
* @note added in QGIS 2.7
5266
*/
5367
void setExpressionsEnabled( const bool enabled );
68+
5469
/** Returns whether the widget will allow entry of simple expressions, which are
5570
* evaluated and then discarded.
5671
* @returns true if spin box allows expression entry
@@ -62,28 +77,30 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
6277
virtual void clear() override;
6378

6479
/**
65-
* @brief setClearValue defines the clear value as a custom value and will automatically set the clear value mode to CustomValue
80+
* Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue.
6681
* @param customValue defines the numerical value used as the clear value
6782
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
83+
* @see setClearValue()
6884
*/
6985
void setClearValue( double customValue, const QString& clearValueText = QString() );
86+
7087
/**
71-
* @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value
88+
* Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
7289
* @param mode mode to user for clear value
7390
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
7491
*/
7592
void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() );
7693

77-
//! returns the value used when clear() is called.
94+
/** Returns the value used when clear() is called.
95+
* @see setClearValue()
96+
*/
7897
double clearValue() const;
7998

8099
virtual double valueFromText( const QString & text ) const override;
81100
virtual QValidator::State validate( QString & input, int & pos ) const override;
82-
83101
void paintEvent( QPaintEvent* e ) override;
84102

85103
protected:
86-
virtual void resizeEvent( QResizeEvent* event ) override;
87104
virtual void changeEvent( QEvent* event ) override;
88105

89106
private slots:
@@ -93,16 +110,15 @@ class GUI_EXPORT QgsDoubleSpinBox : public QDoubleSpinBox
93110
int frameWidth() const;
94111
bool shouldShowClearForValue( const double value ) const;
95112

96-
void updateStyleSheet( const QColor& backgroundColor = QColor() );
113+
QgsSpinBoxLineEdit* mLineEdit;
97114

98115
bool mShowClearButton;
99116
ClearValueMode mClearValueMode;
100117
double mCustomClearValue;
101118

102119
bool mExpressionsEnabled;
103120

104-
QToolButton* mClearButton;
105121
QString stripped( const QString &originalText ) const;
106122
};
107123

108-
#endif // QGSDOUBLESPPINBOX_H
124+
#endif // QGSDOUBLESPINBOX_H

‎src/gui/editorwidgets/qgsspinbox.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
#include <QMouseEvent>
1818
#include <QSettings>
1919
#include <QStyle>
20-
#include <QToolButton>
2120

2221
#include "qgsspinbox.h"
2322
#include "qgsexpression.h"
2423
#include "qgsapplication.h"
2524
#include "qgslogger.h"
25+
#include "qgsfilterlineedit.h"
26+
27+
#define CLEAR_ICON_SIZE 16
2628

2729
QgsSpinBox::QgsSpinBox( QWidget *parent )
2830
: QSpinBox( parent )
@@ -31,25 +33,22 @@ QgsSpinBox::QgsSpinBox( QWidget *parent )
3133
, mCustomClearValue( 0 )
3234
, mExpressionsEnabled( true )
3335
{
34-
mClearButton = new QToolButton( this );
35-
mClearButton->setIcon( QgsApplication::getThemeIcon( "/mIconClear.svg" ) );
36-
mClearButton->setCursor( Qt::ArrowCursor );
37-
mClearButton->setStyleSheet( "position: absolute; border: none; padding: 0px;" );
38-
connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clear() ) );
36+
mLineEdit = new QgsSpinBoxLineEdit();
3937

40-
setStyleSheet( QString( "padding-right: %1px;" ).arg( mClearButton->sizeHint().width() + 18 + frameWidth() + 1 ) );
38+
setLineEdit( mLineEdit );
4139

4240
QSize msz = minimumSizeHint();
43-
setMinimumSize( qMax( msz.width(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ),
44-
qMax( msz.height(), mClearButton->sizeHint().height() + frameWidth() * 2 + 2 ) );
41+
setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2,
42+
qMax( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
4543

44+
connect( mLineEdit, SIGNAL( cleared() ), this, SLOT( clear() ) );
4645
connect( this, SIGNAL( valueChanged( int ) ), this, SLOT( changed( int ) ) );
4746
}
4847

4948
void QgsSpinBox::setShowClearButton( const bool showClearButton )
5049
{
5150
mShowClearButton = showClearButton;
52-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
51+
mLineEdit->setShowClearButton( showClearButton );
5352
}
5453

5554
void QgsSpinBox::setExpressionsEnabled( const bool enabled )
@@ -60,18 +59,18 @@ void QgsSpinBox::setExpressionsEnabled( const bool enabled )
6059
void QgsSpinBox::changeEvent( QEvent *event )
6160
{
6261
QSpinBox::changeEvent( event );
63-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
62+
mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
6463
}
6564

6665
void QgsSpinBox::paintEvent( QPaintEvent *event )
6766
{
68-
mClearButton->setVisible( shouldShowClearForValue( value() ) );
67+
mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
6968
QSpinBox::paintEvent( event );
7069
}
7170

7271
void QgsSpinBox::changed( int value )
7372
{
74-
mClearButton->setVisible( shouldShowClearForValue( value ) );
73+
mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
7574
}
7675

7776
void QgsSpinBox::clear()
@@ -187,14 +186,3 @@ QString QgsSpinBox::stripped( const QString &originalText ) const
187186

188187
return text;
189188
}
190-
191-
void QgsSpinBox::resizeEvent( QResizeEvent * event )
192-
{
193-
QSpinBox::resizeEvent( event );
194-
195-
QSize sz = mClearButton->sizeHint();
196-
197-
mClearButton->move( rect().right() - frameWidth() - 18 - sz.width(),
198-
( rect().bottom() + 1 - sz.height() ) / 2 );
199-
200-
}

‎src/gui/editorwidgets/qgsspinbox.h

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
* *
1414
***************************************************************************/
1515

16-
#ifndef QGSSPPINBOX_H
17-
#define QGSSPPINBOX_H
16+
#ifndef QGSSPINBOX_H
17+
#define QGSSPINBOX_H
1818

1919
#include <QSpinBox>
20-
#include <QToolButton>
20+
21+
class QgsSpinBoxLineEdit;
2122

2223
/** \ingroup gui
2324
* @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
2829
{
2930
Q_OBJECT
3031
Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton )
32+
Q_PROPERTY( bool clearValue READ clearValue WRITE setClearValue )
33+
Q_PROPERTY( bool expressionsEnabled READ expressionsEnabled WRITE setExpressionsEnabled )
3134

3235
public:
36+
37+
//! Behaviour when widget is cleared.
3338
enum ClearValueMode
3439
{
35-
MinimumValue,
36-
MaximumValue,
37-
CustomValue
40+
MinimumValue, //!< Reset value to minimum()
41+
MaximumValue, //!< Reset value to maximum()
42+
CustomValue, //!< Reset value to custom value (see setClearValue() )
3843
};
3944

45+
/** Constructor for QgsSpinBox.
46+
* @param parent parent widget
47+
*/
4048
explicit QgsSpinBox( QWidget *parent = nullptr );
4149

42-
//! determines if the widget will show a clear button
43-
//! @note the clear button will set the widget to its minimum value
50+
/** Sets whether the widget will show a clear button. The clear button
51+
* allows users to reset the widget to a default or empty state.
52+
* @param showClearButton set to true to show the clear button, or false to hide it
53+
* @see showClearButton()
54+
*/
4455
void setShowClearButton( const bool showClearButton );
56+
57+
/** Returns whether the widget is showing a clear button.
58+
* @see setShowClearButton()
59+
*/
4560
bool showClearButton() const {return mShowClearButton;}
4661

4762
/** Sets if the widget will allow entry of simple expressions, which are
@@ -50,6 +65,7 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
5065
* @note added in QGIS 2.7
5166
*/
5267
void setExpressionsEnabled( const bool enabled );
68+
5369
/** Returns whether the widget will allow entry of simple expressions, which are
5470
* evaluated and then discarded.
5571
* @returns true if spin box allows expression entry
@@ -61,26 +77,30 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
6177
virtual void clear() override;
6278

6379
/**
64-
* @brief setClearValue defines the clear value for the widget and will automatically set the clear value mode to CustomValue
80+
* Defines the clear value as a custom value and will automatically set the clear value mode to CustomValue.
6581
* @param customValue defines the numerical value used as the clear value
6682
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
83+
* @see setClearValue()
6784
*/
6885
void setClearValue( int customValue, const QString& clearValueText = QString() );
86+
6987
/**
70-
* @brief setClearValueMode defines if the clear value should be the minimum or maximum values of the widget or a custom value
88+
* Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
7189
* @param mode mode to user for clear value
7290
* @param clearValueText is the text displayed when the spin box is at the clear value. If not specified, no special value text is used.
7391
*/
7492
void setClearValueMode( ClearValueMode mode, const QString& clearValueText = QString() );
7593

76-
//! returns the value used when clear() is called.
94+
/** Returns the value used when clear() is called.
95+
* @see setClearValue()
96+
*/
7797
int clearValue() const;
7898

7999
virtual int valueFromText( const QString & text ) const override;
80100
virtual QValidator::State validate( QString & input, int & pos ) const override;
81101

82102
protected:
83-
virtual void resizeEvent( QResizeEvent* event ) override;
103+
84104
virtual void changeEvent( QEvent* event ) override;
85105
virtual void paintEvent( QPaintEvent* event ) override;
86106

@@ -91,14 +111,15 @@ class GUI_EXPORT QgsSpinBox : public QSpinBox
91111
int frameWidth() const;
92112
bool shouldShowClearForValue( const int value ) const;
93113

114+
QgsSpinBoxLineEdit* mLineEdit;
115+
94116
bool mShowClearButton;
95117
ClearValueMode mClearValueMode;
96118
int mCustomClearValue;
97119

98120
bool mExpressionsEnabled;
99121

100-
QToolButton* mClearButton;
101122
QString stripped( const QString &originalText ) const;
102123
};
103124

104-
#endif // QGSSPPINBOX_H
125+
#endif // QGSSPINBOX_H

‎src/gui/qgsfilterlineedit.cpp

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
QgsFilterLineEdit::QgsFilterLineEdit( QWidget* parent, const QString& nullValue )
2727
: QLineEdit( parent )
28+
, mClearButtonVisible( true )
29+
, mClearMode( ClearToNull )
2830
, mNullValue( nullValue )
2931
, mFocusInEvent( false )
3032
, mClearHover( false )
@@ -40,11 +42,15 @@ QgsFilterLineEdit::QgsFilterLineEdit( QWidget* parent, const QString& nullValue
4042

4143
connect( this, SIGNAL( textChanged( const QString& ) ), this,
4244
SLOT( onTextChanged( const QString& ) ) );
45+
}
4346

44-
int frameWidth = style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
45-
QSize msz = minimumSizeHint();
46-
setMinimumSize( qMax( msz.width(), mClearIconSize.width() + frameWidth * 2 + 2 ),
47-
qMax( msz.height(), mClearIconSize.height() + frameWidth * 2 + 2 ) );
47+
void QgsFilterLineEdit::setShowClearButton( bool visible )
48+
{
49+
mClearButtonVisible = visible;
50+
if ( !visible )
51+
mClearHover = false;
52+
53+
update();
4854
}
4955

5056
void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e )
@@ -56,8 +62,7 @@ void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e )
5662

5763
if ( shouldShowClear() && clearRect().contains( e->pos() ) )
5864
{
59-
clear();
60-
emit cleared();
65+
clearValue();
6166
}
6267
}
6368

@@ -91,10 +96,27 @@ void QgsFilterLineEdit::focusInEvent( QFocusEvent* e )
9196
}
9297
}
9398

94-
void QgsFilterLineEdit::clear()
99+
void QgsFilterLineEdit::clearValue()
95100
{
96-
setText( mNullValue );
101+
switch ( mClearMode )
102+
{
103+
case ClearToNull:
104+
setText( mNullValue );
105+
break;
106+
107+
case ClearToDefault:
108+
setText( mDefaultValue );
109+
break;
110+
}
111+
112+
if ( mClearHover )
113+
{
114+
setCursor( Qt::IBeamCursor );
115+
mClearHover = false;
116+
}
117+
97118
setModified( true );
119+
emit cleared();
98120
}
99121

100122
void QgsFilterLineEdit::paintEvent( QPaintEvent* e )
@@ -134,11 +156,28 @@ void QgsFilterLineEdit::onTextChanged( const QString &text )
134156
setStyleSheet( mStyleSheet );
135157
emit valueChanged( text );
136158
}
159+
160+
if ( mClearHover && !shouldShowClear() )
161+
{
162+
setCursor( Qt::IBeamCursor );
163+
mClearHover = false;
164+
}
137165
}
138166

139167
bool QgsFilterLineEdit::shouldShowClear() const
140168
{
141-
return isEnabled() && !isReadOnly() && !isNull();
169+
if ( !isEnabled() || isReadOnly() || !mClearButtonVisible )
170+
return false;
171+
172+
switch ( mClearMode )
173+
{
174+
case ClearToNull:
175+
return !isNull();
176+
177+
case ClearToDefault:
178+
return value() != mDefaultValue;
179+
}
180+
return false; //avoid warnings
142181
}
143182

144183
QRect QgsFilterLineEdit::clearRect() const

‎src/gui/qgsfilterlineedit.h

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,146 @@
2222

2323
class QToolButton;
2424

25-
/** \ingroup gui
26-
* Lineedit with builtin clear button
25+
/** \class QgsFilterLineEdit
26+
* \ingroup gui
27+
* QLineEdit subclass with built in support for clearing the widget's value and
28+
* handling custom null value representations.
29+
*
30+
* When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used
31+
* instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged()
32+
* signal should be used instead of textChanged().
2733
**/
2834
class GUI_EXPORT QgsFilterLineEdit : public QLineEdit
2935
{
3036
Q_OBJECT
37+
Q_ENUMS( ClearMode )
38+
Q_PROPERTY( ClearMode clearMode READ clearMode WRITE setClearMode )
3139
Q_PROPERTY( QString nullValue READ nullValue WRITE setNullValue )
40+
Q_PROPERTY( QString defaultValue READ defaultValue WRITE setDefaultValue )
41+
Q_PROPERTY( QString value READ value WRITE setValue NOTIFY valueChanged )
42+
Q_PROPERTY( bool showClearButton READ showClearButton WRITE setShowClearButton )
3243

3344
public:
45+
46+
//! Behaviour when clearing value of widget
47+
enum ClearMode
48+
{
49+
ClearToNull = 0, //!< Reset value to null
50+
ClearToDefault, //!< Reset value to default value (see defaultValue() )
51+
};
52+
53+
/** Constructor for QgsFilterLineEdit.
54+
* @param parent parent widget
55+
* @param nullValue string for representing null values
56+
*/
3457
QgsFilterLineEdit( QWidget* parent = nullptr, const QString& nullValue = QString::null );
3558

59+
/** Returns true if the widget's clear button is visible.
60+
* @see setShowClearButton()
61+
* @note added in QGIS 3.0
62+
*/
63+
bool showClearButton() const { return mClearButtonVisible; }
64+
65+
/** Sets whether the widget's clear button is visible.
66+
* @param visible set to false to hide the clear button
67+
* @see showClearButton()
68+
* @note added in QGIS 3.0
69+
*/
70+
void setShowClearButton( bool visible );
71+
72+
/** Returns the clear mode for the widget. The clear mode defines the behaviour of the
73+
* widget when its value is cleared. This defaults to ClearToNull.
74+
* @see setClearMode()
75+
* @note added in QGIS 3.0
76+
*/
77+
ClearMode clearMode() const { return mClearMode; }
78+
79+
/** Sets the clear mode for the widget. The clear mode defines the behaviour of the
80+
* widget when its value is cleared. This defaults to ClearToNull.
81+
* @see clearMode()
82+
* @note added in QGIS 3.0
83+
*/
84+
void setClearMode( ClearMode mode ) { mClearMode = mode; }
85+
86+
/** Sets the string representation for null values in the widget. This does not
87+
* affect the values returned for null values by value(), rather it only affects
88+
* the text that is shown to users when the widget's value is null.
89+
* @param nullValue string to show when widget's value is null
90+
* @see nullValue()
91+
*/
3692
void setNullValue( const QString& nullValue ) { mNullValue = nullValue; }
3793

94+
/** Returns the string used for representating null values in the widget.
95+
* @see setNullValue()
96+
* @see isNull()
97+
*/
3898
QString nullValue() const { return mNullValue; }
3999

100+
/** Sets the default value for the widget. The default value is a value
101+
* which the widget will be reset to if it is cleared and the clearMode()
102+
* is equal to ClearToDefault.
103+
* @param defaultValue default value
104+
* @see defaultValue()
105+
* @see clearMode()
106+
* @note added in QGIS 3.0
107+
*/
108+
void setDefaultValue( const QString& defaultValue ) { mDefaultValue = defaultValue; }
109+
110+
/** Returns the default value for the widget. The default value is a value
111+
* which the widget will be reset to if it is cleared and the clearMode()
112+
* is equal to ClearToDefault.
113+
* @see setDefaultValue()
114+
* @see clearMode()
115+
* @note added in QGIS 3.0
116+
*/
117+
QString defaultValue() const { return mDefaultValue; }
118+
40119
/**
41-
* Sets the current text with NULL support
120+
* Sets the current text for the widget with support for handling null values.
42121
*
43-
* @param value The text to set. If a Null string is provided, the text will match the nullValue.
122+
* @param value The text to set. If a null string is provided, the text shown in the
123+
* widget will be set to the current nullValue().
124+
* @see value()
44125
*/
45126
void setValue( const QString& value ) { setText( value.isNull() ? mNullValue : value ); }
46127

47128
/**
48-
* Returns the text of this edit with NULL support
129+
* Returns the text of this edit with support for handling null values. If the text
130+
* in the widget matches the current nullValue() then the returned value will be
131+
* a null string.
49132
*
50-
* @return Current text (Null string if it matches the nullValue property )
133+
* @return Current text (or null string if it matches the nullValue() property )
134+
* @see setValue()
51135
*/
52136
QString value() const { return isNull() ? QString::null : text(); }
53137

54138
/**
55-
* Determine if the current text represents Null.
139+
* Determine if the current text represents null.
56140
*
57-
* @return True if the value is Null.
141+
* @return True if the widget's value is null.
142+
* @see nullValue()
58143
*/
59144
inline bool isNull() const { return text() == mNullValue; }
60145

146+
public slots:
147+
148+
/** Clears the widget and resets it to the null value.
149+
* @see nullValue()
150+
* @note added in QGIS 3.0
151+
*/
152+
virtual void clearValue();
153+
61154
signals:
155+
156+
/** Emitted when the widget is cleared
157+
* @see clearValue()
158+
*/
62159
void cleared();
63160

64161
/**
65-
* Same as textChanged(const QString& ) but with support for Null values.
162+
* Same as textChanged() but with support for null values.
66163
*
67-
* @param value The current text or Null string if it matches the nullValue property.
164+
* @param value The current text or null string if it matches the nullValue() property.
68165
*/
69166
void valueChanged( const QString& value );
70167

@@ -76,11 +173,16 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit
76173
void leaveEvent( QEvent* e ) override;
77174

78175
private slots:
79-
void clear();
80176
void onTextChanged( const QString &text );
81177

82178
private:
179+
180+
bool mClearButtonVisible;
181+
182+
ClearMode mClearMode;
183+
83184
QString mNullValue;
185+
QString mDefaultValue;
84186
QString mStyleSheet;
85187
bool mFocusInEvent;
86188
bool mClearHover;
@@ -95,4 +197,33 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit
95197
QRect clearRect() const;
96198
};
97199

200+
/// @cond PRIVATE
201+
202+
/** Private QgsFilterLineEdit subclass for use as a line edit in QgsSpinBox/QgsDoubleSpinBox
203+
* we let QgsFilterLineEdit handle display of the clear button and detection
204+
* of clicks, but override clearValue() and let Qgs(Double)SpinBox handle the clearing
205+
* themselves.
206+
*/
207+
class QgsSpinBoxLineEdit : public QgsFilterLineEdit
208+
{
209+
Q_OBJECT
210+
211+
public:
212+
213+
QgsSpinBoxLineEdit( QWidget* parent = nullptr )
214+
: QgsFilterLineEdit( parent )
215+
{}
216+
217+
public slots:
218+
219+
virtual void clearValue() override
220+
{
221+
// don't change the value - let spin boxes handle that by detecting cleared() signal
222+
setCursor( Qt::IBeamCursor );
223+
setModified( true );
224+
emit cleared();
225+
}
226+
};
227+
/// @endcond
228+
98229
#endif // QGSFILTERLINEEDIT_H

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
4646
ADD_PYTHON_TEST(PyQgsProject test_qgsproject.py)
4747
ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py)
4848
ADD_PYTHON_TEST(PyQgsField test_qgsfield.py)
49+
ADD_PYTHON_TEST(PyQgsFilterLineEdit test_qgsfilterlineedit.py)
4950
ADD_PYTHON_TEST(PyQgsFontUtils test_qgsfontutils.py)
5051
ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersections.py)
5152
ADD_PYTHON_TEST(PyQgsGeometryGeneratorSymbolLayer test_qgsgeometrygeneratorsymbollayer.py)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsFilterLineEdit
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '20/08/2016'
11+
__copyright__ = 'Copyright 2016, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.gui import QgsFilterLineEdit
18+
19+
try:
20+
from qgis.PyQt.QtTest import QSignalSpy
21+
use_signal_spy = True
22+
except:
23+
use_signal_spy = False
24+
25+
from qgis.testing import start_app, unittest
26+
27+
start_app()
28+
29+
30+
class TestQgsFilterLineEdit(unittest.TestCase):
31+
32+
def testGettersSetters(self):
33+
""" test widget getters/setters """
34+
w = qgis.gui.QgsFilterLineEdit()
35+
36+
w.setNullValue('null')
37+
self.assertEqual(w.nullValue(), 'null')
38+
w.setValue('value')
39+
self.assertEqual(w.value(), 'value')
40+
self.assertEqual(w.text(), 'value')
41+
w.setDefaultValue('default')
42+
self.assertEqual(w.defaultValue(), 'default')
43+
w.setClearMode(QgsFilterLineEdit.ClearToDefault)
44+
self.assertEqual(w.clearMode(), QgsFilterLineEdit.ClearToDefault)
45+
w.setShowClearButton(False)
46+
self.assertFalse(w.showClearButton())
47+
w.setShowClearButton(True)
48+
self.assertTrue(w.showClearButton())
49+
50+
def testNullValueHandling(self):
51+
""" test widget handling of null values """
52+
w = qgis.gui.QgsFilterLineEdit()
53+
54+
# start with no null value
55+
w.setValue(None)
56+
self.assertTrue(w.isNull())
57+
w.setValue('a')
58+
self.assertEqual(w.text(), 'a')
59+
self.assertFalse(w.isNull())
60+
61+
# set a null value
62+
w.setNullValue('null')
63+
self.assertEqual(w.value(), 'a')
64+
self.assertEqual(w.text(), 'a')
65+
self.assertFalse(w.isNull())
66+
67+
w.setValue(None)
68+
self.assertTrue(w.isNull())
69+
self.assertFalse(w.value())
70+
self.assertEqual(w.text(), 'null')
71+
72+
w.setValue('null')
73+
self.assertEqual(w.text(), 'null')
74+
# ND: I don't think this following logic is correct - should be a distinction between
75+
# the widget's representation of null and the actual value. Ie isNull()
76+
# should be false and value() should return 'null'
77+
# in other words - if you break this test to match my desired behaviour, feel free to remove it!
78+
self.assertTrue(w.isNull())
79+
self.assertFalse(w.value())
80+
81+
def testClearToNull(self):
82+
""" test clearing widget """
83+
w = qgis.gui.QgsFilterLineEdit()
84+
85+
w.setValue('abc')
86+
w.clearValue()
87+
self.assertTrue(w.isNull())
88+
self.assertFalse(w.value())
89+
w.clearValue()
90+
self.assertTrue(w.isNull())
91+
self.assertFalse(w.value())
92+
93+
w.setNullValue('def')
94+
w.setValue('abc')
95+
self.assertFalse(w.isNull())
96+
w.clearValue()
97+
self.assertEqual(w.text(), 'def')
98+
self.assertTrue(w.isNull())
99+
self.assertFalse(w.value())
100+
101+
def testClearToDefault(self):
102+
# test clearing to default value
103+
w = qgis.gui.QgsFilterLineEdit()
104+
w.setClearMode(QgsFilterLineEdit.ClearToDefault)
105+
106+
w.setValue('abc')
107+
w.clearValue()
108+
self.assertTrue(w.isNull())
109+
self.assertFalse(w.value())
110+
w.clearValue()
111+
self.assertTrue(w.isNull())
112+
self.assertFalse(w.value())
113+
114+
w.setDefaultValue('def')
115+
w.setValue('abc')
116+
self.assertFalse(w.isNull())
117+
w.clearValue()
118+
self.assertEqual(w.value(), 'def')
119+
self.assertEqual(w.text(), 'def')
120+
self.assertFalse(w.isNull())
121+
122+
@unittest.skipIf(not use_signal_spy, "No QSignalSpy available")
123+
def test_ChangedSignals(self):
124+
""" test that signals are correctly emitted when clearing"""
125+
126+
w = qgis.gui.QgsFilterLineEdit()
127+
128+
cleared_spy = QSignalSpy(w.cleared)
129+
w.setValue('1')
130+
w.clearValue()
131+
132+
self.assertEqual(len(cleared_spy), 1)
133+
134+
135+
if __name__ == '__main__':
136+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.