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