1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <private/qabstractspinbox_p.h>
41#include <qspinbox.h>
42
43#include <qlineedit.h>
44#include <qlocale.h>
45#include <qvalidator.h>
46#include <qdebug.h>
47
48#include <algorithm>
49#include <cmath>
50#include <float.h>
51
52QT_BEGIN_NAMESPACE
53
54//#define QSPINBOX_QSBDEBUG
55#ifdef QSPINBOX_QSBDEBUG
56# define QSBDEBUG qDebug
57#else
58# define QSBDEBUG if (false) qDebug
59#endif
60
61class QSpinBoxPrivate : public QAbstractSpinBoxPrivate
62{
63 Q_DECLARE_PUBLIC(QSpinBox)
64public:
65 QSpinBoxPrivate();
66 void emitSignals(EmitPolicy ep, const QVariant &) override;
67
68 virtual QVariant valueFromText(const QString &n) const override;
69 virtual QString textFromValue(const QVariant &n) const override;
70 QVariant validateAndInterpret(QString &input, int &pos,
71 QValidator::State &state) const;
72
73 inline void init() {
74 Q_Q(QSpinBox);
75 q->setInputMethodHints(Qt::ImhDigitsOnly);
76 setLayoutItemMargins(element: QStyle::SE_SpinBoxLayoutItem);
77 }
78
79 int displayIntegerBase;
80
81 QVariant calculateAdaptiveDecimalStep(int steps) const override;
82};
83
84class QDoubleSpinBoxPrivate : public QAbstractSpinBoxPrivate
85{
86 Q_DECLARE_PUBLIC(QDoubleSpinBox)
87public:
88 QDoubleSpinBoxPrivate();
89 void emitSignals(EmitPolicy ep, const QVariant &) override;
90
91 virtual QVariant valueFromText(const QString &n) const override;
92 virtual QString textFromValue(const QVariant &n) const override;
93 QVariant validateAndInterpret(QString &input, int &pos,
94 QValidator::State &state) const;
95 double round(double input) const;
96 // variables
97 int decimals;
98
99 inline void init() {
100 Q_Q(QDoubleSpinBox);
101 q->setInputMethodHints(Qt::ImhFormattedNumbersOnly);
102 }
103
104 // When fiddling with the decimals property, we may lose precision in these properties.
105 double actualMin;
106 double actualMax;
107
108 QVariant calculateAdaptiveDecimalStep(int steps) const override;
109};
110
111
112/*!
113 \class QSpinBox
114 \brief The QSpinBox class provides a spin box widget.
115
116 \ingroup basicwidgets
117 \inmodule QtWidgets
118
119 \image windows-spinbox.png
120
121 QSpinBox is designed to handle integers and discrete sets of
122 values (e.g., month names); use QDoubleSpinBox for floating point
123 values.
124
125 QSpinBox allows the user to choose a value by clicking the up/down
126 buttons or pressing up/down on the keyboard to increase/decrease
127 the value currently displayed. The user can also type the value in
128 manually. The spin box supports integer values but can be extended to
129 use different strings with validate(), textFromValue() and valueFromText().
130
131 Every time the value changes QSpinBox emits valueChanged() and
132 textChanged() signals, the former providing a int and the latter
133 a QString. The textChanged() signal provides the value with both
134 prefix() and suffix().
135 The current value can be fetched with value() and set with setValue().
136
137 Clicking the up/down buttons or using the keyboard accelerator's
138 up and down arrows will increase or decrease the current value in
139 steps of size singleStep(). If you want to change this behaviour you
140 can reimplement the virtual function stepBy(). The minimum and
141 maximum value and the step size can be set using one of the
142 constructors, and can be changed later with setMinimum(),
143 setMaximum() and setSingleStep().
144
145 Most spin boxes are directional, but QSpinBox can also operate as
146 a circular spin box, i.e. if the range is 0-99 and the current
147 value is 99, clicking "up" will give 0 if wrapping() is set to
148 true. Use setWrapping() if you want circular behavior.
149
150 The displayed value can be prepended and appended with arbitrary
151 strings indicating, for example, currency or the unit of
152 measurement. See setPrefix() and setSuffix(). The text in the spin
153 box is retrieved with text() (which includes any prefix() and
154 suffix()), or with cleanText() (which has no prefix(), no suffix()
155 and no leading or trailing whitespace).
156
157 It is often desirable to give the user a special (often default)
158 choice in addition to the range of numeric values. See
159 setSpecialValueText() for how to do this with QSpinBox.
160
161 \section1 Subclassing QSpinBox
162
163 If using prefix(), suffix(), and specialValueText() don't provide
164 enough control, you subclass QSpinBox and reimplement
165 valueFromText() and textFromValue(). For example, here's the code
166 for a custom spin box that allows the user to enter icon sizes
167 (e.g., "32 x 32"):
168
169 \snippet widgets/icons/iconsizespinbox.cpp 1
170 \codeline
171 \snippet widgets/icons/iconsizespinbox.cpp 2
172
173 See the \l{widgets/icons}{Icons} example for the full source
174 code.
175
176 \sa QDoubleSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
177*/
178
179/*!
180 \fn void QSpinBox::valueChanged(int i)
181
182 This signal is emitted whenever the spin box's value is changed.
183 The new value's integer value is passed in \a i.
184*/
185
186/*!
187 \fn void QSpinBox::textChanged(const QString &text)
188 \since 5.14
189
190 This signal is emitted whenever the spin box's text is changed.
191 The new text is passed in \a text with prefix() and suffix().
192*/
193
194#if QT_DEPRECATED_SINCE(5, 14)
195/*!
196 \fn void QSpinBox::valueChanged(const QString &text)
197
198 \overload
199 \obsolete Use textChanged(QString) instead
200
201 The new value is passed in \a text with prefix() and suffix().
202*/
203#endif
204
205/*!
206 Constructs a spin box with 0 as minimum value and 99 as maximum value, a
207 step value of 1. The value is initially set to 0. It is parented to \a
208 parent.
209
210 \sa setMinimum(), setMaximum(), setSingleStep()
211*/
212
213QSpinBox::QSpinBox(QWidget *parent)
214 : QAbstractSpinBox(*new QSpinBoxPrivate, parent)
215{
216 Q_D(QSpinBox);
217 d->init();
218}
219
220/*!
221 Destructor.
222*/
223QSpinBox::~QSpinBox() {}
224
225/*!
226 \property QSpinBox::value
227 \brief the value of the spin box
228
229 setValue() will emit valueChanged() if the new value is different
230 from the old one. The value property has a second notifier
231 signal which includes the spin box's prefix and suffix.
232*/
233
234int QSpinBox::value() const
235{
236 Q_D(const QSpinBox);
237 return d->value.toInt();
238}
239
240void QSpinBox::setValue(int value)
241{
242 Q_D(QSpinBox);
243 d->setValue(val: QVariant(value), ep: EmitIfChanged);
244}
245
246/*!
247 \property QSpinBox::prefix
248 \brief the spin box's prefix
249
250 The prefix is prepended to the start of the displayed value.
251 Typical use is to display a unit of measurement or a currency
252 symbol. For example:
253
254 \snippet code/src_gui_widgets_qspinbox.cpp 0
255
256 To turn off the prefix display, set this property to an empty
257 string. The default is no prefix. The prefix is not displayed when
258 value() == minimum() and specialValueText() is set.
259
260 If no prefix is set, prefix() returns an empty string.
261
262 \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText()
263*/
264
265QString QSpinBox::prefix() const
266{
267 Q_D(const QSpinBox);
268 return d->prefix;
269}
270
271void QSpinBox::setPrefix(const QString &prefix)
272{
273 Q_D(QSpinBox);
274
275 d->prefix = prefix;
276 d->updateEdit();
277
278 d->cachedSizeHint = QSize();
279 d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix
280 updateGeometry();
281}
282
283/*!
284 \property QSpinBox::suffix
285 \brief the suffix of the spin box
286
287 The suffix is appended to the end of the displayed value. Typical
288 use is to display a unit of measurement or a currency symbol. For
289 example:
290
291 \snippet code/src_gui_widgets_qspinbox.cpp 1
292
293 To turn off the suffix display, set this property to an empty
294 string. The default is no suffix. The suffix is not displayed for
295 the minimum() if specialValueText() is set.
296
297 If no suffix is set, suffix() returns an empty string.
298
299 \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText()
300*/
301
302QString QSpinBox::suffix() const
303{
304 Q_D(const QSpinBox);
305
306 return d->suffix;
307}
308
309void QSpinBox::setSuffix(const QString &suffix)
310{
311 Q_D(QSpinBox);
312
313 d->suffix = suffix;
314 d->updateEdit();
315
316 d->cachedSizeHint = QSize();
317 updateGeometry();
318}
319
320/*!
321 \property QSpinBox::cleanText
322
323 \brief the text of the spin box excluding any prefix, suffix,
324 or leading or trailing whitespace.
325
326 \sa text, QSpinBox::prefix, QSpinBox::suffix
327*/
328
329QString QSpinBox::cleanText() const
330{
331 Q_D(const QSpinBox);
332
333 return d->stripped(text: d->edit->displayText());
334}
335
336
337/*!
338 \property QSpinBox::singleStep
339 \brief the step value
340
341 When the user uses the arrows to change the spin box's value the
342 value will be incremented/decremented by the amount of the
343 singleStep. The default value is 1. Setting a singleStep value of
344 less than 0 does nothing.
345*/
346
347int QSpinBox::singleStep() const
348{
349 Q_D(const QSpinBox);
350
351 return d->singleStep.toInt();
352}
353
354void QSpinBox::setSingleStep(int value)
355{
356 Q_D(QSpinBox);
357 if (value >= 0) {
358 d->singleStep = QVariant(value);
359 d->updateEdit();
360 }
361}
362
363/*!
364 \property QSpinBox::minimum
365
366 \brief the minimum value of the spin box
367
368 When setting this property the \l maximum is adjusted
369 if necessary to ensure that the range remains valid.
370
371 The default minimum value is 0.
372
373 \sa setRange(), specialValueText
374*/
375
376int QSpinBox::minimum() const
377{
378 Q_D(const QSpinBox);
379
380 return d->minimum.toInt();
381}
382
383void QSpinBox::setMinimum(int minimum)
384{
385 Q_D(QSpinBox);
386 const QVariant m(minimum);
387 d->setRange(min: m, max: (QSpinBoxPrivate::variantCompare(arg1: d->maximum, arg2: m) > 0 ? d->maximum : m));
388}
389
390/*!
391 \property QSpinBox::maximum
392
393 \brief the maximum value of the spin box
394
395 When setting this property the minimum is adjusted
396 if necessary, to ensure that the range remains valid.
397
398 The default maximum value is 99.
399
400 \sa setRange(), specialValueText
401
402*/
403
404int QSpinBox::maximum() const
405{
406 Q_D(const QSpinBox);
407
408 return d->maximum.toInt();
409}
410
411void QSpinBox::setMaximum(int maximum)
412{
413 Q_D(QSpinBox);
414 const QVariant m(maximum);
415 d->setRange(min: (QSpinBoxPrivate::variantCompare(arg1: d->minimum, arg2: m) < 0 ? d->minimum : m), max: m);
416}
417
418/*!
419 Convenience function to set the \a minimum, and \a maximum values
420 with a single function call.
421
422 \snippet code/src_gui_widgets_qspinbox.cpp 2
423 is equivalent to:
424 \snippet code/src_gui_widgets_qspinbox.cpp 3
425
426 \sa minimum, maximum
427*/
428
429void QSpinBox::setRange(int minimum, int maximum)
430{
431 Q_D(QSpinBox);
432 d->setRange(min: QVariant(minimum), max: QVariant(maximum));
433}
434
435/*!
436 Sets the step type for the spin box to \a stepType, which is single
437 step or adaptive decimal step.
438
439 Adaptive decimal step means that the step size will continuously be
440 adjusted to one power of ten below the current \l value. So when
441 the value is 1100, the step is set to 100, so stepping up once
442 increases it to 1200. For 1200 stepping up takes it to 1300. For
443 negative values, stepping down from -1100 goes to -1200.
444
445 Step direction is taken into account to handle edges cases, so
446 that stepping down from 100 takes the value to 99 instead of 90.
447 Thus a step up followed by a step down -- or vice versa -- always
448 lands on the starting value; 99 -> 100 -> 99.
449
450 Setting this will cause the spin box to disregard the value of
451 \l singleStep, although it is preserved so that \l singleStep
452 comes into effect if adaptive decimal step is later turned off.
453
454 \since 5.12
455*/
456
457void QSpinBox::setStepType(QAbstractSpinBox::StepType stepType)
458{
459 Q_D(QSpinBox);
460 d->stepType = stepType;
461}
462
463/*!
464 \property QSpinBox::stepType
465 \brief The step type.
466
467 The step type can be single step or adaptive decimal step.
468*/
469
470QAbstractSpinBox::StepType QSpinBox::stepType() const
471{
472 Q_D(const QSpinBox);
473 return d->stepType;
474}
475
476/*!
477 \property QSpinBox::displayIntegerBase
478
479 \brief the base used to display the value of the spin box
480
481 The default displayIntegerBase value is 10.
482
483 \sa textFromValue(), valueFromText()
484 \since 5.2
485*/
486
487int QSpinBox::displayIntegerBase() const
488{
489 Q_D(const QSpinBox);
490 return d->displayIntegerBase;
491}
492
493void QSpinBox::setDisplayIntegerBase(int base)
494{
495 Q_D(QSpinBox);
496 // Falls back to base 10 on invalid bases (like QString)
497 if (Q_UNLIKELY(base < 2 || base > 36)) {
498 qWarning(msg: "QSpinBox::setDisplayIntegerBase: Invalid base (%d)", base);
499 base = 10;
500 }
501
502 if (base != d->displayIntegerBase) {
503 d->displayIntegerBase = base;
504 d->updateEdit();
505 }
506}
507
508/*!
509 This virtual function is used by the spin box whenever it needs to
510 display the given \a value. The default implementation returns a
511 string containing \a value printed in the standard way using
512 QWidget::locale().toString(), but with the thousand separator
513 removed unless setGroupSeparatorShown() is set. Reimplementations may
514 return anything. (See the example in the detailed description.)
515
516 Note: QSpinBox does not call this function for specialValueText()
517 and that neither prefix() nor suffix() should be included in the
518 return value.
519
520 If you reimplement this, you may also need to reimplement
521 valueFromText() and validate()
522
523 \sa valueFromText(), validate(), QLocale::groupSeparator()
524*/
525
526QString QSpinBox::textFromValue(int value) const
527{
528 Q_D(const QSpinBox);
529 QString str;
530
531 if (d->displayIntegerBase != 10) {
532 const QLatin1String prefix = value < 0 ? QLatin1String("-") : QLatin1String();
533 str = prefix + QString::number(qAbs(t: value), base: d->displayIntegerBase);
534 } else {
535 str = locale().toString(i: value);
536 if (!d->showGroupSeparator && (qAbs(t: value) >= 1000 || value == INT_MIN)) {
537 str.remove(c: locale().groupSeparator());
538 }
539 }
540
541 return str;
542}
543
544/*!
545 \fn int QSpinBox::valueFromText(const QString &text) const
546
547 This virtual function is used by the spin box whenever it needs to
548 interpret \a text entered by the user as a value.
549
550 Subclasses that need to display spin box values in a non-numeric
551 way need to reimplement this function.
552
553 Note: QSpinBox handles specialValueText() separately; this
554 function is only concerned with the other values.
555
556 \sa textFromValue(), validate()
557*/
558
559int QSpinBox::valueFromText(const QString &text) const
560{
561 Q_D(const QSpinBox);
562
563 QString copy = text;
564 int pos = d->edit->cursorPosition();
565 QValidator::State state = QValidator::Acceptable;
566 return d->validateAndInterpret(input&: copy, pos, state).toInt();
567}
568
569/*!
570 \reimp
571*/
572QValidator::State QSpinBox::validate(QString &text, int &pos) const
573{
574 Q_D(const QSpinBox);
575
576 QValidator::State state;
577 d->validateAndInterpret(input&: text, pos, state);
578 return state;
579}
580
581
582/*!
583 \reimp
584*/
585void QSpinBox::fixup(QString &input) const
586{
587 if (!isGroupSeparatorShown())
588 input.remove(c: locale().groupSeparator());
589}
590
591
592// --- QDoubleSpinBox ---
593
594/*!
595 \class QDoubleSpinBox
596 \brief The QDoubleSpinBox class provides a spin box widget that
597 takes doubles.
598
599 \ingroup basicwidgets
600 \inmodule QtWidgets
601
602 QDoubleSpinBox allows the user to choose a value by clicking the
603 up and down buttons or by pressing Up or Down on the keyboard to
604 increase or decrease the value currently displayed. The user can
605 also type the value in manually. The spin box supports double
606 values but can be extended to use different strings with
607 validate(), textFromValue() and valueFromText().
608
609 Every time the value changes QDoubleSpinBox emits valueChanged() and
610 textChanged() signals, the former providing a double and the latter
611 a QString. The textChanged() signal provides the value with both
612 prefix() and suffix(). The current value can be fetched with
613 value() and set with setValue().
614
615 Note: QDoubleSpinBox will round numbers so they can be displayed
616 with the current precision. In a QDoubleSpinBox with decimals set
617 to 2, calling setValue(2.555) will cause value() to return 2.56.
618
619 Clicking the up and down buttons or using the keyboard accelerator's
620 Up and Down arrows will increase or decrease the current value in
621 steps of size singleStep(). If you want to change this behavior you
622 can reimplement the virtual function stepBy(). The minimum and
623 maximum value and the step size can be set using one of the
624 constructors, and can be changed later with setMinimum(),
625 setMaximum() and setSingleStep(). The spinbox has a default
626 precision of 2 decimal places but this can be changed using
627 setDecimals().
628
629 Most spin boxes are directional, but QDoubleSpinBox can also
630 operate as a circular spin box, i.e. if the range is 0.0-99.9 and
631 the current value is 99.9, clicking "up" will give 0 if wrapping()
632 is set to true. Use setWrapping() if you want circular behavior.
633
634 The displayed value can be prepended and appended with arbitrary
635 strings indicating, for example, currency or the unit of
636 measurement. See setPrefix() and setSuffix(). The text in the spin
637 box is retrieved with text() (which includes any prefix() and
638 suffix()), or with cleanText() (which has no prefix(), no suffix()
639 and no leading or trailing whitespace).
640
641 It is often desirable to give the user a special (often default)
642 choice in addition to the range of numeric values. See
643 setSpecialValueText() for how to do this with QDoubleSpinBox.
644
645 \note The displayed value of the QDoubleSpinBox is limited to 18 characters
646 in addition to eventual prefix and suffix content. This limitation is used
647 to keep the double spin box usable even with extremely large values.
648 \sa QSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
649*/
650
651/*!
652 \fn void QDoubleSpinBox::valueChanged(double d);
653
654 This signal is emitted whenever the spin box's value is changed.
655 The new value is passed in \a d.
656*/
657
658/*!
659 \fn void QDoubleSpinBox::textChanged(const QString &text)
660 \since 5.14
661
662 This signal is emitted whenever the spin box's text is changed.
663 The new text is passed in \a text with prefix() and suffix().
664*/
665
666#if QT_DEPRECATED_SINCE(5, 14)
667/*!
668 \fn void QDoubleSpinBox::valueChanged(const QString &text);
669
670 \overload
671 \obsolete Use textChanged(QString) instead
672
673 The new value is passed in \a text with prefix() and suffix().
674*/
675#endif
676
677/*!
678 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
679 a step value of 1.0 and a precision of 2 decimal places. The value is
680 initially set to 0.00. The spin box has the given \a parent.
681
682 \sa setMinimum(), setMaximum(), setSingleStep()
683*/
684QDoubleSpinBox::QDoubleSpinBox(QWidget *parent)
685 : QAbstractSpinBox(*new QDoubleSpinBoxPrivate, parent)
686{
687 Q_D(QDoubleSpinBox);
688 d->init();
689}
690
691/*!
692 Destructor.
693*/
694QDoubleSpinBox::~QDoubleSpinBox() {}
695
696/*!
697 \property QDoubleSpinBox::value
698 \brief the value of the spin box
699
700 setValue() will emit valueChanged() if the new value is different
701 from the old one. The value property has a second notifier
702 signal which includes the spin box's prefix and suffix.
703
704 Note: The value will be rounded so it can be displayed with the
705 current setting of decimals.
706
707 \sa decimals
708*/
709double QDoubleSpinBox::value() const
710{
711 Q_D(const QDoubleSpinBox);
712
713 return d->value.toDouble();
714}
715
716void QDoubleSpinBox::setValue(double value)
717{
718 Q_D(QDoubleSpinBox);
719 QVariant v(d->round(input: value));
720 d->setValue(val: v, ep: EmitIfChanged);
721}
722/*!
723 \property QDoubleSpinBox::prefix
724 \brief the spin box's prefix
725
726 The prefix is prepended to the start of the displayed value.
727 Typical use is to display a unit of measurement or a currency
728 symbol. For example:
729
730 \snippet code/src_gui_widgets_qspinbox.cpp 4
731
732 To turn off the prefix display, set this property to an empty
733 string. The default is no prefix. The prefix is not displayed when
734 value() == minimum() and specialValueText() is set.
735
736 If no prefix is set, prefix() returns an empty string.
737
738 \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText()
739*/
740
741QString QDoubleSpinBox::prefix() const
742{
743 Q_D(const QDoubleSpinBox);
744
745 return d->prefix;
746}
747
748void QDoubleSpinBox::setPrefix(const QString &prefix)
749{
750 Q_D(QDoubleSpinBox);
751
752 d->prefix = prefix;
753 d->updateEdit();
754
755 d->cachedSizeHint = QSize();
756 d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix
757 updateGeometry();
758}
759
760/*!
761 \property QDoubleSpinBox::suffix
762 \brief the suffix of the spin box
763
764 The suffix is appended to the end of the displayed value. Typical
765 use is to display a unit of measurement or a currency symbol. For
766 example:
767
768 \snippet code/src_gui_widgets_qspinbox.cpp 5
769
770 To turn off the suffix display, set this property to an empty
771 string. The default is no suffix. The suffix is not displayed for
772 the minimum() if specialValueText() is set.
773
774 If no suffix is set, suffix() returns an empty string.
775
776 \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText()
777*/
778
779QString QDoubleSpinBox::suffix() const
780{
781 Q_D(const QDoubleSpinBox);
782
783 return d->suffix;
784}
785
786void QDoubleSpinBox::setSuffix(const QString &suffix)
787{
788 Q_D(QDoubleSpinBox);
789
790 d->suffix = suffix;
791 d->updateEdit();
792
793 d->cachedSizeHint = QSize();
794 updateGeometry();
795}
796
797/*!
798 \property QDoubleSpinBox::cleanText
799
800 \brief the text of the spin box excluding any prefix, suffix,
801 or leading or trailing whitespace.
802
803 \sa text, QDoubleSpinBox::prefix, QDoubleSpinBox::suffix
804*/
805
806QString QDoubleSpinBox::cleanText() const
807{
808 Q_D(const QDoubleSpinBox);
809
810 return d->stripped(text: d->edit->displayText());
811}
812
813/*!
814 \property QDoubleSpinBox::singleStep
815 \brief the step value
816
817 When the user uses the arrows to change the spin box's value the
818 value will be incremented/decremented by the amount of the
819 singleStep. The default value is 1.0. Setting a singleStep value
820 of less than 0 does nothing.
821*/
822double QDoubleSpinBox::singleStep() const
823{
824 Q_D(const QDoubleSpinBox);
825
826 return d->singleStep.toDouble();
827}
828
829void QDoubleSpinBox::setSingleStep(double value)
830{
831 Q_D(QDoubleSpinBox);
832
833 if (value >= 0) {
834 d->singleStep = value;
835 d->updateEdit();
836 }
837}
838
839/*!
840 \property QDoubleSpinBox::minimum
841
842 \brief the minimum value of the spin box
843
844 When setting this property the \l maximum is adjusted
845 if necessary to ensure that the range remains valid.
846
847 The default minimum value is 0.0.
848
849 Note: The minimum value will be rounded to match the decimals
850 property.
851
852 \sa decimals, setRange(), specialValueText
853*/
854
855double QDoubleSpinBox::minimum() const
856{
857 Q_D(const QDoubleSpinBox);
858
859 return d->minimum.toDouble();
860}
861
862void QDoubleSpinBox::setMinimum(double minimum)
863{
864 Q_D(QDoubleSpinBox);
865 d->actualMin = minimum;
866 const QVariant m(d->round(input: minimum));
867 d->setRange(min: m, max: (QDoubleSpinBoxPrivate::variantCompare(arg1: d->maximum, arg2: m) > 0 ? d->maximum : m));
868}
869
870/*!
871 \property QDoubleSpinBox::maximum
872
873 \brief the maximum value of the spin box
874
875 When setting this property the \l minimum is adjusted
876 if necessary, to ensure that the range remains valid.
877
878 The default maximum value is 99.99.
879
880 Note: The maximum value will be rounded to match the decimals
881 property.
882
883 \sa decimals, setRange()
884*/
885
886double QDoubleSpinBox::maximum() const
887{
888 Q_D(const QDoubleSpinBox);
889
890 return d->maximum.toDouble();
891}
892
893void QDoubleSpinBox::setMaximum(double maximum)
894{
895 Q_D(QDoubleSpinBox);
896 d->actualMax = maximum;
897 const QVariant m(d->round(input: maximum));
898 d->setRange(min: (QDoubleSpinBoxPrivate::variantCompare(arg1: d->minimum, arg2: m) < 0 ? d->minimum : m), max: m);
899}
900
901/*!
902 Convenience function to set the \a minimum and \a maximum values
903 with a single function call.
904
905 Note: The maximum and minimum values will be rounded to match the
906 decimals property.
907
908 \snippet code/src_gui_widgets_qspinbox.cpp 6
909 is equivalent to:
910 \snippet code/src_gui_widgets_qspinbox.cpp 7
911
912 \sa minimum, maximum
913*/
914
915void QDoubleSpinBox::setRange(double minimum, double maximum)
916{
917 Q_D(QDoubleSpinBox);
918 d->actualMin = minimum;
919 d->actualMax = maximum;
920 d->setRange(min: QVariant(d->round(input: minimum)), max: QVariant(d->round(input: maximum)));
921}
922
923/*!
924 Sets the step type for the spin box to \a stepType, which is single
925 step or adaptive decimal step.
926
927 Adaptive decimal step means that the step size will continuously be
928 adjusted to one power of ten below the current \l value. So when
929 the value is 1100, the step is set to 100, so stepping up once
930 increases it to 1200. For 1200 stepping up takes it to 1300. For
931 negative values, stepping down from -1100 goes to -1200.
932
933 It also works for any decimal values, 0.041 is increased to 0.042
934 by stepping once.
935
936 Step direction is taken into account to handle edges cases, so
937 that stepping down from 100 takes the value to 99 instead of 90.
938 Thus a step up followed by a step down -- or vice versa -- always
939 lands on the starting value; 99 -> 100 -> 99.
940
941 Setting this will cause the spin box to disregard the value of
942 \l singleStep, although it is preserved so that \l singleStep
943 comes into effect if adaptive decimal step is later turned off.
944
945 \since 5.12
946*/
947
948void QDoubleSpinBox::setStepType(StepType stepType)
949{
950 Q_D(QDoubleSpinBox);
951 d->stepType = stepType;
952}
953
954/*!
955 \property QDoubleSpinBox::stepType
956 \brief The step type.
957
958 The step type can be single step or adaptive decimal step.
959*/
960
961QAbstractSpinBox::StepType QDoubleSpinBox::stepType() const
962{
963 Q_D(const QDoubleSpinBox);
964 return d->stepType;
965}
966
967/*!
968 \property QDoubleSpinBox::decimals
969
970 \brief the precision of the spin box, in decimals
971
972 Sets how many decimals the spinbox will use for displaying and
973 interpreting doubles.
974
975 \warning The maximum value for \a decimals is DBL_MAX_10_EXP +
976 DBL_DIG (ie. 323) because of the limitations of the double type.
977
978 Note: The maximum, minimum and value might change as a result of
979 changing this property.
980*/
981
982int QDoubleSpinBox::decimals() const
983{
984 Q_D(const QDoubleSpinBox);
985
986 return d->decimals;
987}
988
989void QDoubleSpinBox::setDecimals(int decimals)
990{
991 Q_D(QDoubleSpinBox);
992 d->decimals = qBound(min: 0, val: decimals, DBL_MAX_10_EXP + DBL_DIG);
993
994 setRange(minimum: d->actualMin, maximum: d->actualMax); // make sure values are rounded
995 setValue(value());
996}
997
998/*!
999 This virtual function is used by the spin box whenever it needs to
1000 display the given \a value. The default implementation returns a string
1001 containing \a value printed using QWidget::locale().toString(\a value,
1002 QLatin1Char('f'), decimals()) and will remove the thousand
1003 separator unless setGroupSeparatorShown() is set. Reimplementations may
1004 return anything.
1005
1006 Note: QDoubleSpinBox does not call this function for
1007 specialValueText() and that neither prefix() nor suffix() should
1008 be included in the return value.
1009
1010 If you reimplement this, you may also need to reimplement
1011 valueFromText().
1012
1013 \sa valueFromText(), QLocale::groupSeparator()
1014*/
1015
1016
1017QString QDoubleSpinBox::textFromValue(double value) const
1018{
1019 Q_D(const QDoubleSpinBox);
1020 QString str = locale().toString(i: value, f: 'f', prec: d->decimals);
1021 if (!d->showGroupSeparator && qAbs(t: value) >= 1000.0)
1022 str.remove(c: locale().groupSeparator());
1023
1024 return str;
1025}
1026
1027/*!
1028 This virtual function is used by the spin box whenever it needs to
1029 interpret \a text entered by the user as a value.
1030
1031 Subclasses that need to display spin box values in a non-numeric
1032 way need to reimplement this function.
1033
1034 Note: QDoubleSpinBox handles specialValueText() separately; this
1035 function is only concerned with the other values.
1036
1037 \sa textFromValue(), validate()
1038*/
1039double QDoubleSpinBox::valueFromText(const QString &text) const
1040{
1041 Q_D(const QDoubleSpinBox);
1042
1043 QString copy = text;
1044 int pos = d->edit->cursorPosition();
1045 QValidator::State state = QValidator::Acceptable;
1046 return d->validateAndInterpret(input&: copy, pos, state).toDouble();
1047}
1048
1049/*!
1050 \reimp
1051*/
1052QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const
1053{
1054 Q_D(const QDoubleSpinBox);
1055
1056 QValidator::State state;
1057 d->validateAndInterpret(input&: text, pos, state);
1058 return state;
1059}
1060
1061
1062/*!
1063 \reimp
1064*/
1065void QDoubleSpinBox::fixup(QString &input) const
1066{
1067 input.remove(c: locale().groupSeparator());
1068}
1069
1070// --- QSpinBoxPrivate ---
1071
1072/*!
1073 \internal
1074 Constructs a QSpinBoxPrivate object
1075*/
1076
1077QSpinBoxPrivate::QSpinBoxPrivate()
1078{
1079 minimum = QVariant((int)0);
1080 maximum = QVariant((int)99);
1081 value = minimum;
1082 displayIntegerBase = 10;
1083 singleStep = QVariant((int)1);
1084 type = QMetaType::Int;
1085}
1086
1087/*!
1088 \internal
1089 \reimp
1090*/
1091
1092void QSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
1093{
1094 Q_Q(QSpinBox);
1095 if (ep != NeverEmit) {
1096 pendingEmit = false;
1097 if (ep == AlwaysEmit || value != old) {
1098#if QT_DEPRECATED_SINCE(5, 14)
1099QT_WARNING_PUSH
1100QT_WARNING_DISABLE_DEPRECATED
1101 emit q->valueChanged(edit->displayText());
1102QT_WARNING_POP
1103#endif
1104 emit q->textChanged(edit->displayText());
1105 emit q->valueChanged(value.toInt());
1106 }
1107 }
1108}
1109
1110/*!
1111 \internal
1112 \reimp
1113*/
1114
1115QString QSpinBoxPrivate::textFromValue(const QVariant &value) const
1116{
1117 Q_Q(const QSpinBox);
1118 return q->textFromValue(value: value.toInt());
1119}
1120/*!
1121 \internal
1122 \reimp
1123*/
1124
1125QVariant QSpinBoxPrivate::valueFromText(const QString &text) const
1126{
1127 Q_Q(const QSpinBox);
1128
1129 return QVariant(q->valueFromText(text));
1130}
1131
1132
1133/*!
1134 \internal Multi purpose function that parses input, sets state to
1135 the appropriate state and returns the value it will be interpreted
1136 as.
1137*/
1138
1139QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
1140 QValidator::State &state) const
1141{
1142 if (cachedText == input && !input.isEmpty()) {
1143 state = cachedState;
1144 QSBDEBUG() << "cachedText was '" << cachedText << "' state was "
1145 << state << " and value was " << cachedValue;
1146
1147 return cachedValue;
1148 }
1149 const int max = maximum.toInt();
1150 const int min = minimum.toInt();
1151
1152 QString copy = stripped(text: input, pos: &pos);
1153 QSBDEBUG() << "input" << input << "copy" << copy;
1154 state = QValidator::Acceptable;
1155 int num = min;
1156
1157 if (max != min && (copy.isEmpty()
1158 || (min < 0 && copy == QLatin1String("-"))
1159 || (max >= 0 && copy == QLatin1String("+")))) {
1160 state = QValidator::Intermediate;
1161 QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
1162 } else if (copy.startsWith(c: QLatin1Char('-')) && min >= 0) {
1163 state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100
1164 } else {
1165 bool ok = false;
1166 if (displayIntegerBase != 10) {
1167 num = copy.toInt(ok: &ok, base: displayIntegerBase);
1168 } else {
1169 num = locale.toInt(s: copy, ok: &ok);
1170 if (!ok && (max >= 1000 || min <= -1000)) {
1171 const QString sep(locale.groupSeparator());
1172 const QString doubleSep = sep + sep;
1173 if (copy.contains(s: sep) && !copy.contains(s: doubleSep)) {
1174 QString copy2 = copy;
1175 copy2.remove(s: sep);
1176 num = locale.toInt(s: copy2, ok: &ok);
1177 }
1178 }
1179 }
1180 QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
1181 if (!ok) {
1182 state = QValidator::Invalid;
1183 } else if (num >= min && num <= max) {
1184 state = QValidator::Acceptable;
1185 } else if (max == min) {
1186 state = QValidator::Invalid;
1187 } else {
1188 if ((num >= 0 && num > max) || (num < 0 && num < min)) {
1189 state = QValidator::Invalid;
1190 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1191 } else {
1192 state = QValidator::Intermediate;
1193 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate";
1194 }
1195 }
1196 }
1197 if (state != QValidator::Acceptable)
1198 num = max > 0 ? min : max;
1199 input = prefix + copy + suffix;
1200 cachedText = input;
1201 cachedState = state;
1202 cachedValue = QVariant((int)num);
1203
1204 QSBDEBUG() << "cachedText is set to '" << cachedText << "' state is set to "
1205 << state << " and value is set to " << cachedValue;
1206 return cachedValue;
1207}
1208
1209QVariant QSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1210{
1211 const int intValue = value.toInt();
1212 const int absValue = qAbs(t: intValue);
1213
1214 if (absValue < 100)
1215 return 1;
1216
1217 const bool valueNegative = intValue < 0;
1218 const bool stepsNegative = steps < 0;
1219 const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1;
1220
1221 const int log = static_cast<int>(std::log10(x: absValue - signCompensation)) - 1;
1222 return static_cast<int>(std::pow(x: 10, y: log));
1223}
1224
1225// --- QDoubleSpinBoxPrivate ---
1226
1227/*!
1228 \internal
1229 Constructs a QSpinBoxPrivate object
1230*/
1231
1232QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate()
1233{
1234 actualMin = 0.0;
1235 actualMax = 99.99;
1236 minimum = QVariant(actualMin);
1237 maximum = QVariant(actualMax);
1238 value = minimum;
1239 singleStep = QVariant(1.0);
1240 decimals = 2;
1241 type = QMetaType::Double;
1242}
1243
1244/*!
1245 \internal
1246 \reimp
1247*/
1248
1249void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
1250{
1251 Q_Q(QDoubleSpinBox);
1252 if (ep != NeverEmit) {
1253 pendingEmit = false;
1254 if (ep == AlwaysEmit || value != old) {
1255#if QT_DEPRECATED_SINCE(5, 14)
1256QT_WARNING_PUSH
1257QT_WARNING_DISABLE_DEPRECATED
1258 emit q->valueChanged(edit->displayText());
1259QT_WARNING_POP
1260#endif
1261 emit q->textChanged(edit->displayText());
1262 emit q->valueChanged(value.toDouble());
1263 }
1264 }
1265}
1266
1267
1268/*!
1269 \internal
1270 \reimp
1271*/
1272QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const
1273{
1274 Q_Q(const QDoubleSpinBox);
1275 return QVariant(q->valueFromText(text: f));
1276}
1277
1278/*!
1279 \internal
1280 Rounds to a double value that is restricted to decimals.
1281 E.g. // decimals = 2
1282
1283 round(5.555) => 5.56
1284 */
1285
1286double QDoubleSpinBoxPrivate::round(double value) const
1287{
1288 return QString::number(value, f: 'f', prec: decimals).toDouble();
1289}
1290
1291
1292/*!
1293 \internal Multi purpose function that parses input, sets state to
1294 the appropriate state and returns the value it will be interpreted
1295 as.
1296*/
1297
1298QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
1299 QValidator::State &state) const
1300{
1301 if (cachedText == input && !input.isEmpty()) {
1302 state = cachedState;
1303 QSBDEBUG() << "cachedText was '" << cachedText << "' state was "
1304 << state << " and value was " << cachedValue;
1305 return cachedValue;
1306 }
1307 const double max = maximum.toDouble();
1308 const double min = minimum.toDouble();
1309
1310 QString copy = stripped(text: input, pos: &pos);
1311 QSBDEBUG() << "input" << input << "copy" << copy;
1312 int len = copy.size();
1313 double num = min;
1314 const bool plus = max >= 0;
1315 const bool minus = min <= 0;
1316
1317 const QString group(locale.groupSeparator());
1318 const uint groupUcs = (group.size() > 1 && group.at(i: 0).isHighSurrogate()
1319 ? QChar::surrogateToUcs4(high: group.at(i: 0), low: group.at(i: 1))
1320 : group.at(i: 0).unicode());
1321 switch (len) {
1322 case 0:
1323 state = max != min ? QValidator::Intermediate : QValidator::Invalid;
1324 goto end;
1325 case 1:
1326 if (copy.at(i: 0) == locale.decimalPoint()
1327 || (plus && copy.at(i: 0) == QLatin1Char('+'))
1328 || (minus && copy.at(i: 0) == QLatin1Char('-'))) {
1329 state = QValidator::Intermediate;
1330 goto end;
1331 }
1332 break;
1333 case 2:
1334 if (copy.at(i: 1) == locale.decimalPoint()
1335 && ((plus && copy.at(i: 0) == QLatin1Char('+')) || (minus && copy.at(i: 0) == QLatin1Char('-')))) {
1336 state = QValidator::Intermediate;
1337 goto end;
1338 }
1339 break;
1340 default: break;
1341 }
1342
1343 if (copy.at(i: 0) == locale.groupSeparator()) {
1344 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1345 state = QValidator::Invalid;
1346 goto end;
1347 } else if (len > 1) {
1348 const int dec = copy.indexOf(c: locale.decimalPoint());
1349 if (dec != -1) {
1350 if (dec + 1 < copy.size() && copy.at(i: dec + 1) == locale.decimalPoint() && pos == dec + 1) {
1351 copy.remove(i: dec + 1, len: 1); // typing a delimiter when you are on the delimiter
1352 } // should be treated as typing right arrow
1353
1354 if (copy.size() - dec > decimals + 1) {
1355 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1356 state = QValidator::Invalid;
1357 goto end;
1358 }
1359 for (int i=dec + 1; i<copy.size(); ++i) {
1360 if (copy.at(i).isSpace() || copy.at(i) == locale.groupSeparator()) {
1361 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1362 state = QValidator::Invalid;
1363 goto end;
1364 }
1365 }
1366 } else {
1367 const QChar last = copy.back();
1368 const bool groupEnd = copy.endsWith(s: group);
1369 const QStringView head(copy.constData(), groupEnd ? len - group.size() : len - 1);
1370 const QChar secondLast = head.back();
1371 if ((groupEnd || last.isSpace()) && (head.endsWith(s: group) || secondLast.isSpace())) {
1372 state = QValidator::Invalid;
1373 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1374 goto end;
1375 } else if (last.isSpace() && (!QChar::isSpace(ucs4: groupUcs) || secondLast.isSpace())) {
1376 state = QValidator::Invalid;
1377 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1378 goto end;
1379 }
1380 }
1381 }
1382
1383 {
1384 bool ok = false;
1385 num = locale.toDouble(s: copy, ok: &ok);
1386 QSBDEBUG() << __FILE__ << __LINE__ << locale << copy << num << ok;
1387
1388 if (!ok) {
1389 if (QChar::isPrint(ucs4: groupUcs)) {
1390 if (max < 1000 && min > -1000 && copy.contains(s: group)) {
1391 state = QValidator::Invalid;
1392 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1393 goto end;
1394 }
1395
1396 const int len = copy.size();
1397 for (int i = 0; i < len - 1;) {
1398 if (QStringView(copy).mid(pos: i).startsWith(s: group)) {
1399 if (QStringView(copy).mid(pos: i + group.size()).startsWith(s: group)) {
1400 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1401 state = QValidator::Invalid;
1402 goto end;
1403 }
1404 i += group.size();
1405 } else {
1406 i++;
1407 }
1408 }
1409
1410 QString copy2 = copy;
1411 copy2.remove(s: group);
1412 num = locale.toDouble(s: copy2, ok: &ok);
1413 QSBDEBUG() << group << num << copy2 << ok;
1414
1415 if (!ok) {
1416 state = QValidator::Invalid;
1417 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1418 goto end;
1419 }
1420 }
1421 }
1422
1423 if (!ok) {
1424 state = QValidator::Invalid;
1425 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1426 } else if (num >= min && num <= max) {
1427 state = QValidator::Acceptable;
1428 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Acceptable";
1429 } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min)
1430 state = QValidator::Invalid;
1431 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1432 } else {
1433 if ((num >= 0 && num > max) || (num < 0 && num < min)) {
1434 state = QValidator::Invalid;
1435 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1436 } else {
1437 state = QValidator::Intermediate;
1438 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate";
1439 }
1440 }
1441 }
1442
1443end:
1444 if (state != QValidator::Acceptable) {
1445 num = max > 0 ? min : max;
1446 }
1447
1448 input = prefix + copy + suffix;
1449 cachedText = input;
1450 cachedState = state;
1451 cachedValue = QVariant(num);
1452 return QVariant(num);
1453}
1454
1455/*
1456 \internal
1457 \reimp
1458*/
1459
1460QString QDoubleSpinBoxPrivate::textFromValue(const QVariant &f) const
1461{
1462 Q_Q(const QDoubleSpinBox);
1463 return q->textFromValue(value: f.toDouble());
1464}
1465
1466QVariant QDoubleSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1467{
1468 const double doubleValue = value.toDouble();
1469 const double minStep = std::pow(x: 10, y: -decimals);
1470 double absValue = qAbs(t: doubleValue);
1471
1472 if (absValue < minStep)
1473 return minStep;
1474
1475 const bool valueNegative = doubleValue < 0;
1476 const bool stepsNegative = steps < 0;
1477 if (valueNegative != stepsNegative)
1478 absValue /= 1.01;
1479
1480 const double shift = std::pow(x: 10, y: 1 - std::floor(x: std::log10(x: absValue)));
1481 const double absRounded = round(value: absValue * shift) / shift;
1482 const double log = floorf(x: std::log10(x: absRounded)) - 1;
1483
1484 return std::max(a: minStep, b: std::pow(x: 10, y: log));
1485}
1486
1487/*! \reimp */
1488bool QSpinBox::event(QEvent *event)
1489{
1490 Q_D(QSpinBox);
1491 if (event->type() == QEvent::StyleChange
1492#ifdef Q_OS_MAC
1493 || event->type() == QEvent::MacSizeChange
1494#endif
1495 )
1496 d->setLayoutItemMargins(element: QStyle::SE_SpinBoxLayoutItem);
1497 return QAbstractSpinBox::event(event);
1498}
1499
1500QT_END_NAMESPACE
1501
1502#include "moc_qspinbox.cpp"
1503

source code of qtbase/src/widgets/widgets/qspinbox.cpp