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