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