QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsdoublespinbox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdoublespinbox.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 "qgsdoublespinbox.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
38
40 : QDoubleSpinBox( parent )
41{
42 mLineEdit = new QgsSpinBoxLineEdit();
43
44 // By default, group separator is off
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, &QgsDoubleSpinBox::clear );
52 connect( this, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsDoubleSpinBox::changed );
53}
54
55void QgsDoubleSpinBox::setShowClearButton( const bool showClearButton )
56{
57 mShowClearButton = showClearButton;
58 mLineEdit->setShowClearButton( showClearButton );
59}
60
62{
63 mExpressionsEnabled = enabled;
64}
65
66void QgsDoubleSpinBox::changeEvent( QEvent *event )
67{
68 QDoubleSpinBox::changeEvent( event );
69
70 if ( event->type() == QEvent::FontChange )
71 {
72 lineEdit()->setFont( font() );
73 }
74
75 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
76}
77
78void QgsDoubleSpinBox::wheelEvent( QWheelEvent *event )
79{
80 const double step = singleStep();
81 if ( event->modifiers() & Qt::ControlModifier )
82 {
83 // ctrl modifier results in finer increments - 10% of usual step
84 double newStep = step / 10;
85 // but don't ever use an increment smaller than would be visible in the widget
86 // i.e. if showing 2 decimals, smallest increment will be 0.01
87 newStep = std::max( newStep, std::pow( 10.0, 0.0 - decimals() ) );
88
89 setSingleStep( newStep );
90
91 // clear control modifier before handing off event - Qt uses it for unwanted purposes
92 // (*increasing* step size, whereas QGIS UX convention is that control modifier
93 // results in finer changes!)
94 event->setModifiers( event->modifiers() & ~Qt::ControlModifier );
95 }
96 QDoubleSpinBox::wheelEvent( event );
97 setSingleStep( step );
98}
99
100void QgsDoubleSpinBox::timerEvent( QTimerEvent *event )
101{
102 // Process all events, which may include a mouse release event
103 // Only allow the timer to trigger additional value changes if the user
104 // has in fact held the mouse button, rather than the timer expiry
105 // simply appearing before the mouse release in the event queue
106 qApp->processEvents();
107 if ( QApplication::mouseButtons() & Qt::LeftButton )
108 QDoubleSpinBox::timerEvent( event );
109}
110
111void QgsDoubleSpinBox::paintEvent( QPaintEvent *event )
112{
113 mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
114 QDoubleSpinBox::paintEvent( event );
115}
116
117void QgsDoubleSpinBox::changed( double value )
118{
119 mLineEdit->setShowClearButton( shouldShowClearForValue( value ) );
120}
121
123{
124 setValue( clearValue() );
125 if ( mLineEdit->isNull() )
126 mLineEdit->clear();
127}
128
129void QgsDoubleSpinBox::setClearValue( double customValue, const QString &specialValueText )
130{
131 if ( mClearValueMode == CustomValue && mCustomClearValue == customValue && QAbstractSpinBox::specialValueText() == specialValueText )
132 {
133 return;
134 }
135
136 mClearValueMode = CustomValue;
137 mCustomClearValue = customValue;
138
139 if ( !specialValueText.isEmpty() )
140 {
141 const double v = value();
142 clear();
143 setSpecialValueText( specialValueText );
144 setValue( v );
145 }
146}
147
149{
150 if ( mClearValueMode == mode && mCustomClearValue == 0 && QAbstractSpinBox::specialValueText() == clearValueText )
151 {
152 return;
153 }
154
155 mClearValueMode = mode;
156 mCustomClearValue = 0;
157
158 if ( !clearValueText.isEmpty() )
159 {
160 const double v = value();
161 clear();
162 setSpecialValueText( clearValueText );
163 setValue( v );
164 }
165}
166
168{
169 if ( mClearValueMode == MinimumValue )
170 return minimum();
171 else if ( mClearValueMode == MaximumValue )
172 return maximum();
173 else
174 return mCustomClearValue;
175}
176
177void QgsDoubleSpinBox::setLineEditAlignment( Qt::Alignment alignment )
178{
179 mLineEdit->setAlignment( alignment );
180}
181
183{
184 if ( txt.isEmpty() )
185 {
186 QDoubleSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
187 mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
188 }
189 else
190 {
191 QDoubleSpinBox::setSpecialValueText( txt );
192 mLineEdit->setNullValue( txt );
193 }
194}
195
196QString QgsDoubleSpinBox::stripped( const QString &originalText ) const
197{
198 //adapted from QAbstractSpinBoxPrivate::stripped
199 //trims whitespace, prefix and suffix from spin box text
200 QString text = originalText;
201 if ( specialValueText().isEmpty() || text != specialValueText() )
202 {
203 // Strip SPECIAL_TEXT_WHEN_EMPTY
204 if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
205 text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
206 int from = 0;
207 int size = text.size();
208 bool changed = false;
209 if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
210 {
211 from += prefix().size();
212 size -= from;
213 changed = true;
214 }
215 if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
216 {
217 size -= suffix().size();
218 changed = true;
219 }
220 if ( changed )
221 text = text.mid( from, size );
222 }
223
224 text = text.trimmed();
225
226 return text;
227}
228
229double QgsDoubleSpinBox::valueFromText( const QString &text ) const
230{
231 if ( !mExpressionsEnabled )
232 {
233 return QDoubleSpinBox::valueFromText( text );
234 }
235
236 const QString trimmedText = stripped( text );
237 if ( trimmedText.isEmpty() )
238 {
239 return mShowClearButton ? clearValue() : value();
240 }
241
242 return QgsExpression::evaluateToDouble( trimmedText, value() );
243}
244
245QValidator::State QgsDoubleSpinBox::validate( QString &input, int &pos ) const
246{
247 if ( !mExpressionsEnabled )
248 {
249 const QValidator::State r = QDoubleSpinBox::validate( input, pos );
250 return r;
251 }
252
253 return QValidator::Acceptable;
254}
255
256int QgsDoubleSpinBox::frameWidth() const
257{
258 return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
259}
260
261bool QgsDoubleSpinBox::shouldShowClearForValue( const double value ) const
262{
263 if ( !mShowClearButton || !isEnabled() )
264 {
265 return false;
266 }
267 return value != clearValue();
268}
void paintEvent(QPaintEvent *e) override
void wheelEvent(QWheelEvent *event) override
void setLineEditAlignment(Qt::Alignment alignment)
Set alignment in the embedded line edit widget.
double valueFromText(const QString &text) const override
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 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.
void changeEvent(QEvent *event) override
void clear() override
Sets the current value to the value defined by the clear value.
ClearValueMode
Behavior when widget is cleared.
@ MaximumValue
Reset value to maximum()
@ CustomValue
Reset value to custom value (see setClearValue() )
@ MinimumValue
Reset value to minimum()
QValidator::State validate(QString &input, int &pos) const override
QgsDoubleSpinBox(QWidget *parent=nullptr)
Constructor for QgsDoubleSpinBox.
void setExpressionsEnabled(bool enabled)
Sets if the widget will allow entry of simple expressions, which are evaluated and then discarded.
void setClearValue(double customValue, const QString &clearValueText=QString())
Defines the clear value as a custom value and will automatically set the clear value mode to CustomVa...
void setShowClearButton(bool showClearButton)
Sets whether the widget will show a clear button.
void timerEvent(QTimerEvent *event) override
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.
#define CLEAR_ICON_SIZE