QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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 "moc_qgsspinbox.cpp"
23#include "qgsexpression.h"
24#include "qgsapplication.h"
25#include "qgslogger.h"
26#include "qgsfilterlineedit.h"
27
28#define CLEAR_ICON_SIZE 16
29
30// This is required because private implementation of
31// QAbstractSpinBoxPrivate checks for specialText emptiness
32// and skips specialText handling if it's empty
33#ifdef _MSC_VER
34static QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
35#else
36static constexpr QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
37#endif
38
39QgsSpinBox::QgsSpinBox( QWidget *parent )
40 : QSpinBox( parent )
41{
42 mLineEdit = new QgsSpinBoxLineEdit();
43 connect( mLineEdit, &QLineEdit::returnPressed, this, &QgsSpinBox::returnPressed );
44 connect( mLineEdit, &QLineEdit::textEdited, this, &QgsSpinBox::textEdited );
45 setLineEdit( mLineEdit );
46
47 const QSize msz = minimumSizeHint();
48 setMinimumSize( msz.width() + CLEAR_ICON_SIZE + 9 + frameWidth() * 2 + 2,
49 std::max( msz.height(), CLEAR_ICON_SIZE + frameWidth() * 2 + 2 ) );
50
51 connect( mLineEdit, &QgsFilterLineEdit::cleared, this, &QgsSpinBox::clear );
52 connect( this, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsSpinBox::changed );
53}
54
55void QgsSpinBox::setShowClearButton( const bool showClearButton )
56{
57 mShowClearButton = showClearButton;
58 mLineEdit->setShowClearButton( showClearButton );
59}
60
61void QgsSpinBox::setExpressionsEnabled( const bool enabled )
62{
63 mExpressionsEnabled = enabled;
64}
65
66void QgsSpinBox::changeEvent( QEvent *event )
67{
68 QSpinBox::changeEvent( event );
69
70 if ( event->type() == QEvent::FontChange )
71 {
72 lineEdit()->setFont( font() );
73 }
74
75 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
76}
77
78void QgsSpinBox::paintEvent( QPaintEvent *event )
79{
80 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
81 QSpinBox::paintEvent( event );
82}
83
84void QgsSpinBox::wheelEvent( QWheelEvent *event )
85{
86 const int step = singleStep();
87 if ( event->modifiers() & Qt::ControlModifier )
88 {
89 // ctrl modifier results in finer increments - 10% of usual step
90 int newStep = step / 10;
91 // step should be at least 1
92 newStep = std::max( newStep, 1 );
93
94 setSingleStep( newStep );
95
96 // clear control modifier before handing off event - Qt uses it for unwanted purposes
97 // (*increasing* step size, whereas QGIS UX convention is that control modifier
98 // results in finer changes!)
99 event->setModifiers( event->modifiers() & ~Qt::ControlModifier );
100 }
101 QSpinBox::wheelEvent( event );
102 setSingleStep( step );
103}
104
105void QgsSpinBox::timerEvent( QTimerEvent *event )
106{
107 // Process all events, which may include a mouse release event
108 // Only allow the timer to trigger additional value changes if the user
109 // has in fact held the mouse button, rather than the timer expiry
110 // simply appearing before the mouse release in the event queue
111 qApp->processEvents();
112 if ( QApplication::mouseButtons() & Qt::LeftButton )
113 QSpinBox::timerEvent( event );
114}
115
116void QgsSpinBox::changed( int value )
117{
118 mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
119}
120
122{
123 setValue( clearValue() );
124 if ( mLineEdit->isNull() )
125 mLineEdit->clear();
126}
127
128void QgsSpinBox::setClearValue( int customValue, const QString &specialValueText )
129{
130 if ( mClearValueMode == CustomValue && mCustomClearValue == customValue && QAbstractSpinBox::specialValueText() == specialValueText )
131 {
132 return;
133 }
134
135 mClearValueMode = CustomValue;
136 mCustomClearValue = customValue;
137
138 if ( !specialValueText.isEmpty() )
139 {
140 const int v = value();
141 clear();
142 setSpecialValueText( specialValueText );
143 setValue( v );
144 }
145}
146
147void QgsSpinBox::setClearValueMode( QgsSpinBox::ClearValueMode mode, const QString &specialValueText )
148{
149 if ( mClearValueMode == mode && mCustomClearValue == 0 && QAbstractSpinBox::specialValueText() == specialValueText )
150 {
151 return;
152 }
153
154 mClearValueMode = mode;
155 mCustomClearValue = 0;
156
157 if ( !specialValueText.isEmpty() )
158 {
159 const int v = value();
160 clear();
161 setSpecialValueText( specialValueText );
162 setValue( v );
163 }
164}
165
167{
168 if ( mClearValueMode == MinimumValue )
169 return minimum();
170 else if ( mClearValueMode == MaximumValue )
171 return maximum();
172 else
173 return mCustomClearValue;
174}
175
176void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
177{
178 mLineEdit->setAlignment( alignment );
179}
180
181void QgsSpinBox::setSpecialValueText( const QString &txt )
182{
183 if ( txt.isEmpty() )
184 {
185 QSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
186 mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
187 }
188 else
189 {
190 QSpinBox::setSpecialValueText( txt );
191 mLineEdit->setNullValue( txt );
192 }
193}
194
195int QgsSpinBox::valueFromText( const QString &text ) const
196{
197 if ( !mExpressionsEnabled )
198 {
199 return QSpinBox::valueFromText( text );
200 }
201
202 const QString trimmedText = stripped( text );
203 if ( trimmedText.isEmpty() )
204 {
205 return mShowClearButton ? clearValue() : value();
206 }
207
208 return std::round( QgsExpression::evaluateToDouble( trimmedText, value() ) );
209}
210
211QValidator::State QgsSpinBox::validate( QString &input, int &pos ) const
212{
213 if ( !mExpressionsEnabled )
214 {
215 const QValidator::State r = QSpinBox::validate( input, pos );
216 return r;
217 }
218
219 return QValidator::Acceptable;
220}
221
222void QgsSpinBox::stepBy( int steps )
223{
224 const bool wasNull = mShowClearButton && value() == clearValue();
225 if ( wasNull && minimum() < 0 && maximum() > 0 && !( specialValueText().isEmpty() || specialValueText() == SPECIAL_TEXT_WHEN_EMPTY ) )
226 {
227 // value is currently null, and range allows both positive and negative numbers
228 // in this case we DON'T do the default step, as that would add one step to the NULL value,
229 // which is usually the minimum acceptable value... so the user will get a very large negative number!
230 // Instead, treat the initial value as 0 instead, and then perform the step.
231 whileBlocking( this )->setValue( 0 );
232 }
233 QSpinBox::stepBy( steps );
234}
235
236int QgsSpinBox::frameWidth() const
237{
238 return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
239}
240
241bool QgsSpinBox::shouldShowClearForValue( const int value ) const
242{
243 if ( !mShowClearButton || !isEnabled() )
244 {
245 return false;
246 }
247 return value != clearValue();
248}
249
250QString QgsSpinBox::stripped( const QString &originalText ) const
251{
252 //adapted from QAbstractSpinBoxPrivate::stripped
253 //trims whitespace, prefix and suffix from spin box text
254 QString text = originalText;
255 if ( specialValueText().isEmpty() || text != specialValueText() )
256 {
257 // Strip SPECIAL_TEXT_WHEN_EMPTY
258 if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
259 text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
260 int from = 0;
261 int size = text.size();
262 bool changed = false;
263 if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
264 {
265 from += prefix().size();
266 size -= from;
267 changed = true;
268 }
269 if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
270 {
271 size -= suffix().size();
272 changed = true;
273 }
274 if ( changed )
275 text = text.mid( from, size );
276 }
277
278 text = text.trimmed();
279
280 return text;
281}
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.
void returnPressed()
Emitted when the Return or Enter key is used in the line edit.
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
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
QgsSpinBox(QWidget *parent=nullptr)
Constructor for QgsSpinBox.
QValidator::State validate(QString &input, int &pos) const override
void stepBy(int steps) override
void textEdited(const QString &text)
Emitted when the the value has been manually edited via line edit.
void paintEvent(QPaintEvent *event) override
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.
int valueFromText(const QString &text) const override
void timerEvent(QTimerEvent *event) override
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...
void setExpressionsEnabled(bool enabled)
Sets if the widget will allow entry of simple expressions, which are evaluated and then discarded.
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...
void changeEvent(QEvent *event) override
void clear() override
Sets the current value to the value defined by the clear value.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5862
#define CLEAR_ICON_SIZE