QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsspinbox.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsspinbox.cpp
3  --------------------------------------
4  Date : 09.2014
5  Copyright : (C) 2014 Denis Rouzaud
6  Email : [email protected]
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
33 static QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
34 #else
35 static constexpr QChar SPECIAL_TEXT_WHEN_EMPTY = QChar( 0x2063 );
36 #endif
37 
38 QgsSpinBox::QgsSpinBox( QWidget *parent )
39  : QSpinBox( parent )
40 {
41  mLineEdit = new QgsSpinBoxLineEdit();
42  setLineEdit( mLineEdit );
43 
44  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 
52 void QgsSpinBox::setShowClearButton( const bool showClearButton )
53 {
54  mShowClearButton = showClearButton;
55  mLineEdit->setShowClearButton( showClearButton );
56 }
57 
58 void QgsSpinBox::setExpressionsEnabled( const bool enabled )
59 {
60  mExpressionsEnabled = enabled;
61 }
62 
63 void 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 
75 void QgsSpinBox::paintEvent( QPaintEvent *event )
76 {
77  mLineEdit->setShowClearButton( shouldShowClearForValue( value() ) );
78  QSpinBox::paintEvent( event );
79 }
80 
81 void QgsSpinBox::wheelEvent( QWheelEvent *event )
82 {
83  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 
102 void 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 
113 void 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 
125 void QgsSpinBox::setClearValue( int customValue, const QString &specialValueText )
126 {
127  mClearValueMode = CustomValue;
128  mCustomClearValue = customValue;
129 
130  if ( !specialValueText.isEmpty() )
131  {
132  int v = value();
133  clear();
134  setSpecialValueText( specialValueText );
135  setValue( v );
136  }
137 }
138 
139 void QgsSpinBox::setClearValueMode( QgsSpinBox::ClearValueMode mode, const QString &specialValueText )
140 {
141  mClearValueMode = mode;
142  mCustomClearValue = 0;
143 
144  if ( !specialValueText.isEmpty() )
145  {
146  int v = value();
147  clear();
148  setSpecialValueText( specialValueText );
149  setValue( v );
150  }
151 }
152 
154 {
155  if ( mClearValueMode == MinimumValue )
156  return minimum();
157  else if ( mClearValueMode == MaximumValue )
158  return maximum();
159  else
160  return mCustomClearValue;
161 }
162 
163 void QgsSpinBox::setLineEditAlignment( Qt::Alignment alignment )
164 {
165  mLineEdit->setAlignment( alignment );
166 }
167 
168 void QgsSpinBox::setSpecialValueText( const QString &txt )
169 {
170  if ( txt.isEmpty() )
171  {
172  QSpinBox::setSpecialValueText( SPECIAL_TEXT_WHEN_EMPTY );
173  mLineEdit->setNullValue( SPECIAL_TEXT_WHEN_EMPTY );
174  }
175  else
176  {
177  QSpinBox::setSpecialValueText( txt );
178  mLineEdit->setNullValue( txt );
179  }
180 }
181 
182 int QgsSpinBox::valueFromText( const QString &text ) const
183 {
184  if ( !mExpressionsEnabled )
185  {
186  return QSpinBox::valueFromText( text );
187  }
188 
189  QString trimmedText = stripped( text );
190  if ( trimmedText.isEmpty() )
191  {
192  return mShowClearButton ? clearValue() : value();
193  }
194 
195  return std::round( QgsExpression::evaluateToDouble( trimmedText, value() ) );
196 }
197 
198 QValidator::State QgsSpinBox::validate( QString &input, int &pos ) const
199 {
200  if ( !mExpressionsEnabled )
201  {
202  QValidator::State r = QSpinBox::validate( input, pos );
203  return r;
204  }
205 
206  return QValidator::Acceptable;
207 }
208 
209 int QgsSpinBox::frameWidth() const
210 {
211  return style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
212 }
213 
214 bool QgsSpinBox::shouldShowClearForValue( const int value ) const
215 {
216  if ( !mShowClearButton || !isEnabled() )
217  {
218  return false;
219  }
220  return value != clearValue();
221 }
222 
223 QString QgsSpinBox::stripped( const QString &originalText ) const
224 {
225  //adapted from QAbstractSpinBoxPrivate::stripped
226  //trims whitespace, prefix and suffix from spin box text
227  QString text = originalText;
228  if ( specialValueText().isEmpty() || text != specialValueText() )
229  {
230  // Strip SPECIAL_TEXT_WHEN_EMPTY
231  if ( text.contains( SPECIAL_TEXT_WHEN_EMPTY ) )
232  text = text.replace( SPECIAL_TEXT_WHEN_EMPTY, QString() );
233  int from = 0;
234  int size = text.size();
235  bool changed = false;
236  if ( !prefix().isEmpty() && text.startsWith( prefix() ) )
237  {
238  from += prefix().size();
239  size -= from;
240  changed = true;
241  }
242  if ( !suffix().isEmpty() && text.endsWith( suffix() ) )
243  {
244  size -= suffix().size();
245  changed = true;
246  }
247  if ( changed )
248  text = text.mid( from, size );
249  }
250 
251  text = text.trimmed();
252 
253  return text;
254 }
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:163
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:198
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:139
int valueFromText(const QString &text) const override
Definition: qgsspinbox.cpp:182
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:168
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