Use QgsFilterLineEdit for Qgs(Double)SpinBox

Fixes issues with the current approach:
- poor appearance on certain environments (esp OSX)
- large area on spin boxes' right which "swallows" clicks
and blocks interactivity

Fix #12920
This commit is contained in:
Nyall Dawson 2016-09-08 21:15:06 +10:00
parent 228ff23d81
commit f16b3870fa
7 changed files with 183 additions and 96 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

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

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