QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsspinbox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsspinbox.cpp
3 --------------------------------------
4 Date : 09.2014
5 Copyright : (C) 2014 Denis Rouzaud
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include <QLineEdit>
17#include <QMouseEvent>
18#include <QSettings>
19#include <QStyle>
20
21#include "qgsspinbox.h"
22#include "qgsexpression.h"
23#include "qgsapplication.h"
24#include "qgslogger.h"
25#include "qgsfilterlineedit.h"
26
27#define CLEAR_ICON_SIZE 16
28
29// This is required because private implementation of
30// QAbstractSpinBoxPrivate checks for specialText emptiness
31// and skips specialText handling if it's empty
32#ifdef _MSC_VER
33static QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
34#else
35static constexpr QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
36#endif
37
38QgsSpinBox::QgsSpinBox( QWidget *parent )
39 : QSpinBox( parent )
40{
41 mLineEdit = new QgsSpinBoxLineEdit();
42 setLineEdit( mLineEdit );
43
44 const QSize msz = minimumSizeHint();
45 setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2,
46 std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
47
48 connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsSpinBox::clear );
49 connect( this, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsSpinBox::changed );
50}
51
52void QgsSpinBox::setShowClearButton( const bool showClearButton )
53{
54 mShowClearButton = showClearButton;
55 mLineEdit->setShowClearButton( showClearButton );
56}
57
58void QgsSpinBox::setExpressionsEnabled( const bool enabled )
59{
60 mExpressionsEnabled = enabled;
61}
62
63void QgsSpinBox::changeEvent( QEvent *event )
64{
65 QSpinBox::changeEvent( event );
66
67 if ( event->type() == QEvent::FontChange )
68 {
69 lineEdit()->setFont( font() );
70 }
71
72 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
73}
74
75void QgsSpinBox::paintEvent( QPaintEvent *event )
76{
77 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
78 QSpinBox::paintEvent( event );
79}
80
81void QgsSpinBox::wheelEvent( QWheelEvent *event )
82{
83 const int step = singleStep();
84 if ( event->modifiers() & Qt::ControlModifier )
85 {
86 // ctrl modifier results in finer increments - 10% of usual step
87 int newStep = step / 10;
88 // step should be at least 1
89 newStep = std::max( newStep, 1 );
90
91 setSingleStep( newStep );
92
93 // clear control modifier before handing off event - Qt uses it for unwanted purposes
94 // (*increasing* step size, whereas QGIS UX convention is that control modifier
95 // results in finer changes!)
96 event->setModifiers( event->modifiers() & ~Qt::ControlModifier );
97 }
98 QSpinBox::wheelEvent( event );
99 setSingleStep( step );
100}
101
102void QgsSpinBox::timerEvent( QTimerEvent *event )
103{
104 // Process all events, which may include a mouse release event
105 // Only allow the timer to trigger additional value changes if the user
106 // has in fact held the mouse button, rather than the timer expiry
107 // simply appearing before the mouse release in the event queue
108 qApp->processEvents();
109 if ( QApplication::mouseButtons() & Qt::LeftButton )
110 QSpinBox::timerEvent( event );
111}
112
113void QgsSpinBox::changed( int value )
114{
115 mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
116}
117
119{
120 setValue( clearValue() );
121 if ( mLineEdit->isNull() )
122 mLineEdit->clear();
123}
124
125void QgsSpinBox::setClearValue( int customValue, const QString &specialValueText )
126{
127 if ( mClearValueMode == CustomValue && mCustomClearValue == customValue && QAbstractSpinBox::specialValueText() == specialValueText )
128 {
129 return;
130 }
131
132 mClearValueMode = CustomValue;
133 mCustomClearValue = customValue;
134
135 if ( !specialValueText.isEmpty() )
136 {
137 const int v = value();
138 clear();
139 setSpecialValueText( specialValueText );
140 setValue( v );
141 }
142}
143
144void QgsSpinBox::setClearValueMode( QgsSpinBox::ClearValueMode mode, const QString &specialValueText )
145{
146 if ( mClearValueMode == mode && mCustomClearValue == 0 && QAbstractSpinBox::specialValueText() == specialValueText )
147 {
148 return;
149 }
150
151 mClearValueMode = mode;
152 mCustomClearValue = 0;
153
154 if ( !specialValueText.isEmpty() )
155 {
156 const int v = value();
157 clear();
158 setSpecialValueText( specialValueText );
159 setValue( v );
160 }
161}
162
164{
165 if ( mClearValueMode == MinimumValue )
166 return minimum();
167 else if ( mClearValueMode == MaximumValue )
168 return maximum();
169 else
170 return mCustomClearValue;
171}
172
173void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
174{
175 mLineEdit->setAlignment( alignment );
176}
177
178void QgsSpinBox::setSpecialValueText( const QString &txt )
179{
180 if ( txt.isEmpty() )
181 {
182 QSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
183 mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
184 }
185 else
186 {
187 QSpinBox::setSpecialValueText( txt );
188 mLineEdit->setNullValue( txt );
189 }
190}
191
192int QgsSpinBox::valueFromText( const QString &text ) const
193{
194 if ( !mExpressionsEnabled )
195 {
196 return QSpinBox::valueFromText( text );
197 }
198
199 const QString trimmedText = stripped( text );
200 if ( trimmedText.isEmpty() )
201 {
202 return mShowClearButton ? clearValue() : value();
203 }
204
205 return std::round( QgsExpression::evaluateToDouble( trimmedText, value() ) );
206}
207
208QValidator::State QgsSpinBox::validate( QString &input, int &pos ) const
209{
210 if ( !mExpressionsEnabled )
211 {
212 const QValidator::State r = QSpinBox::validate( input, pos );
213 return r;
214 }
215
216 return QValidator::Acceptable;
217}
218
219int QgsSpinBox::frameWidth() const
220{
221 return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
222}
223
224bool QgsSpinBox::shouldShowClearForValue( const int value ) const
225{
226 if ( !mShowClearButton || !isEnabled() )
227 {
228 return false;
229 }
230 return value != clearValue();
231}
232
233QString QgsSpinBox::stripped( const QString &originalText ) const
234{
235 //adapted from QAbstractSpinBoxPrivate::stripped
236 //trims whitespace, prefix and suffix from spin box text
237 QString text = originalText;
238 if ( specialValueText().isEmpty() || text != specialValueText() )
239 {
240 // Strip SPECIAL_TEXT_WHEN_EMPTY
241 if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
242 text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
243 int from = 0;
244 int size = text.size();
245 bool changed = false;
246 if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
247 {
248 from += prefix().size();
249 size -= from;
250 changed = true;
251 }
252 if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
253 {
254 size -= suffix().size();
255 changed = true;
256 }
257 if ( changed )
258 text = text.mid( from, size );
259 }
260
261 text = text.trimmed();
262
263 return text;
264}
static double evaluateToDouble(const QString &text, double fallbackValue)
Attempts to evaluate a text string as an expression to a resultant double value.
void cleared()
Emitted when the widget is cleared.
void setLineEditAlignment(Qt::Alignment alignment)
Set alignment in the embedded line edit widget.
Definition: qgsspinbox.cpp:173
ClearValueMode
Behavior when widget is cleared.
Definition: qgsspinbox.h:63
@ MaximumValue
Reset value to maximum()
Definition: qgsspinbox.h:65
@ MinimumValue
Reset value to minimum()
Definition: qgsspinbox.h:64
@ CustomValue
Reset value to custom value (see setClearValue() )
Definition: qgsspinbox.h:66
bool showClearButton
Definition: qgsspinbox.h:55
void wheelEvent(QWheelEvent *event) override
Definition: qgsspinbox.cpp:81
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
Definition: qgsspinbox.cpp:52
QgsSpinBox(QWidget *parent=nullptr)
Constructor for QgsSpinBox.
Definition: qgsspinbox.cpp:38
QValidator::State validate(QString &input, int &pos) const override
Definition: qgsspinbox.cpp:208
void paintEvent(QPaintEvent *event) override
Definition: qgsspinbox.cpp:75
bool clearValue
Definition: qgsspinbox.h:56
void setClearValueMode(ClearValueMode mode, const QString &clearValueText=QString())
Defines if the clear value should be the minimum or maximum values of the widget or a custom value.
Definition: qgsspinbox.cpp:144
int valueFromText(const QString &text) const override
Definition: qgsspinbox.cpp:192
void timerEvent(QTimerEvent *event) override
Definition: qgsspinbox.cpp:102
void setClearValue(int customValue, const QString &clearValueText=QString())
Defines the clear value as a custom value and will automatically set the clear value mode to CustomVa...
Definition: qgsspinbox.cpp:125
void setExpressionsEnabled(bool enabled)
Sets if the widget will allow entry of simple expressions, which are evaluated and then discarded.
Definition: qgsspinbox.cpp:58
void setSpecialValueText(const QString &txt)
Set the special-value text to be txt If set, the spin box will display this text instead of a numeric...
Definition: qgsspinbox.cpp:178
void changeEvent(QEvent *event) override
Definition: qgsspinbox.cpp:63
void clear() override
Sets the current value to the value defined by the clear value.
Definition: qgsspinbox.cpp:118
#define CLEAR_ICON_SIZE
Definition: qgsspinbox.cpp:27