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 <qlineedit.h>
9#include <qlocale.h>
10#include <qvalidator.h>
11#include <qdebug.h>
12
13#include <algorithm>
14#include <cmath>
15#include <float.h>
16
17QT_BEGIN_NAMESPACE
18
19using namespace Qt::StringLiterals;
20
21//#define QSPINBOX_QSBDEBUG
22#ifdef QSPINBOX_QSBDEBUG
23# define QSBDEBUG qDebug
24#else
25# define QSBDEBUG if (false) qDebug
26#endif
27
29{
30 Q_DECLARE_PUBLIC(QSpinBox)
31public:
33 void emitSignals(EmitPolicy ep, const QVariant &) override;
34
35 virtual QVariant valueFromText(const QString &n) const override;
36 virtual QString textFromValue(const QVariant &n) const override;
37 QVariant validateAndInterpret(QString &input, int &pos,
38 QValidator::State &state) const;
39
40 inline void init() {
41 Q_Q(QSpinBox);
42 q->setInputMethodHints(Qt::ImhDigitsOnly);
43 setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
44 }
45
47
48 QVariant calculateAdaptiveDecimalStep(int steps) const override;
49};
50
52{
53 Q_DECLARE_PUBLIC(QDoubleSpinBox)
54public:
56 void emitSignals(EmitPolicy ep, const QVariant &) override;
57
58 virtual QVariant valueFromText(const QString &n) const override;
59 virtual QString textFromValue(const QVariant &n) const override;
60 QVariant validateAndInterpret(QString &input, int &pos,
61 QValidator::State &state) const;
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().toString(), but with the thousand separator
466 removed unless setGroupSeparatorShown() is set. Reimplementations may
467 return anything. (See the example in the detailed description.)
468
469 Note: QSpinBox does not call this function for specialValueText()
470 and that neither prefix() nor suffix() should be included in the
471 return value.
472
473 If you reimplement this, you may also need to reimplement
474 valueFromText() and validate()
475
476 \sa valueFromText(), validate(), QLocale::groupSeparator()
477*/
478
479QString QSpinBox::textFromValue(int value) const
480{
481 Q_D(const QSpinBox);
482 QString str;
483
484 if (d->displayIntegerBase != 10) {
485 const auto prefix = value < 0 ? "-"_L1 : ""_L1;
486 str = prefix + QString::number(qAbs(value), d->displayIntegerBase);
487 } else {
488 QLocale loc = locale();
489 if (d->showGroupSeparator)
490 loc.setNumberOptions(loc.numberOptions() & ~QLocale::OmitGroupSeparator);
491 else
492 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator);
493 str = loc.toString(value);
494 }
495
496 return str;
497}
498
499/*!
500 \fn int QSpinBox::valueFromText(const QString &text) const
501
502 This virtual function is used by the spin box whenever it needs to
503 interpret \a text entered by the user as a value.
504
505 Subclasses that need to display spin box values in a non-numeric
506 way need to reimplement this function.
507
508 Note: QSpinBox handles specialValueText() separately; this
509 function is only concerned with the other values.
510
511 \sa textFromValue(), validate()
512*/
513
514int QSpinBox::valueFromText(const QString &text) const
515{
516 Q_D(const QSpinBox);
517
518 QString copy = text;
519 int pos = d->edit->cursorPosition();
520 QValidator::State state = QValidator::Acceptable;
521 return d->validateAndInterpret(copy, pos, state).toInt();
522}
523
524/*!
525 \reimp
526*/
527QValidator::State QSpinBox::validate(QString &text, int &pos) const
528{
529 Q_D(const QSpinBox);
530
531 QValidator::State state;
532 d->validateAndInterpret(text, pos, state);
533 return state;
534}
535
536
537/*!
538 \reimp
539*/
540void QSpinBox::fixup(QString &input) const
541{
542 if (!isGroupSeparatorShown())
543 input.remove(locale().groupSeparator());
544}
545
546
547// --- QDoubleSpinBox ---
548
549/*!
550 \class QDoubleSpinBox
551 \brief The QDoubleSpinBox class provides a spin box widget that
552 takes doubles.
553
554 \ingroup basicwidgets
555 \inmodule QtWidgets
556
557 \image fusion-doublespinbox.png {Spin box displaying a double}
558
559 QDoubleSpinBox allows the user to choose a value by clicking the
560 up and down buttons or by pressing Up or Down on the keyboard to
561 increase or decrease the value currently displayed. The user can
562 also type the value in manually. The spin box supports double
563 values but can be extended to use different strings with
564 validate(), textFromValue() and valueFromText().
565
566 Every time the value changes QDoubleSpinBox emits valueChanged() and
567 textChanged() signals, the former providing a double and the latter
568 a QString. The textChanged() signal provides the value with both
569 prefix() and suffix(). The current value can be fetched with
570 value() and set with setValue().
571
572 Note: QDoubleSpinBox will round numbers so they can be displayed
573 with the current precision. In a QDoubleSpinBox with decimals set
574 to 2, calling setValue(2.555) will cause value() to return 2.56.
575
576 Clicking the up and down buttons or using the keyboard accelerator's
577 Up and Down arrows will increase or decrease the current value in
578 steps of size singleStep(). If you want to change this behavior you
579 can reimplement the virtual function stepBy(). The minimum and
580 maximum value and the step size can be set using one of the
581 constructors, and can be changed later with setMinimum(),
582 setMaximum() and setSingleStep(). The spinbox has a default
583 precision of 2 decimal places but this can be changed using
584 setDecimals().
585
586 Most spin boxes are directional, but QDoubleSpinBox can also
587 operate as a circular spin box, i.e. if the range is 0.0-99.9 and
588 the current value is 99.9, clicking "up" will give 0 if wrapping()
589 is set to true. Use setWrapping() if you want circular behavior.
590
591 The displayed value can be prepended and appended with arbitrary
592 strings indicating, for example, currency or the unit of
593 measurement. See setPrefix() and setSuffix(). The text in the spin
594 box is retrieved with text() (which includes any prefix() and
595 suffix()), or with cleanText() (which has no prefix(), no suffix()
596 and no leading or trailing whitespace).
597
598 It is often desirable to give the user a special (often default)
599 choice in addition to the range of numeric values. See
600 setSpecialValueText() for how to do this with QDoubleSpinBox.
601
602 \note The displayed value of the QDoubleSpinBox is limited to 18 characters
603 in addition to eventual prefix and suffix content. This limitation is used
604 to keep the double spin box usable even with extremely large values.
605 \sa QSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
606*/
607
608/*!
609 \fn void QDoubleSpinBox::valueChanged(double d);
610
611 This signal is emitted whenever the spin box's value is changed.
612 The new value is passed in \a d.
613*/
614
615/*!
616 \fn void QDoubleSpinBox::textChanged(const QString &text)
617 \since 5.14
618
619 This signal is emitted whenever the spin box's text is changed.
620 The new text is passed in \a text with prefix() and suffix().
621*/
622
623/*!
624 Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value,
625 a step value of 1.0 and a precision of 2 decimal places. The value is
626 initially set to 0.00. The spin box has the given \a parent.
627
628 \sa setMinimum(), setMaximum(), setSingleStep()
629*/
630QDoubleSpinBox::QDoubleSpinBox(QWidget *parent)
631 : QAbstractSpinBox(*new QDoubleSpinBoxPrivate, parent)
632{
633 Q_D(QDoubleSpinBox);
634 d->init();
635}
636
637/*!
638 Destructor.
639*/
640QDoubleSpinBox::~QDoubleSpinBox() {}
641
642/*!
643 \property QDoubleSpinBox::value
644 \brief the value of the spin box
645
646 setValue() will emit valueChanged() if the new value is different
647 from the old one. The value property has a second notifier
648 signal which includes the spin box's prefix and suffix.
649
650 Note: The value will be rounded so it can be displayed with the
651 current setting of decimals.
652
653 \sa decimals
654*/
655double QDoubleSpinBox::value() const
656{
657 Q_D(const QDoubleSpinBox);
658
659 return d->value.toDouble();
660}
661
662void QDoubleSpinBox::setValue(double value)
663{
664 Q_D(QDoubleSpinBox);
665 QVariant v(d->round(value));
666 d->setValue(v, EmitIfChanged);
667}
668/*!
669 \property QDoubleSpinBox::prefix
670 \brief the spin box's prefix
671
672 The prefix is prepended to the start of the displayed value.
673 Typical use is to display a unit of measurement or a currency
674 symbol. For example:
675
676 \snippet code/src_gui_widgets_qspinbox.cpp 4
677
678 To turn off the prefix display, set this property to an empty
679 string. The default is no prefix. The prefix is not displayed when
680 value() == minimum() and specialValueText() is set.
681
682 If no prefix is set, prefix() returns an empty string.
683
684 \sa suffix(), setSuffix(), specialValueText(), setSpecialValueText()
685*/
686
687QString QDoubleSpinBox::prefix() const
688{
689 Q_D(const QDoubleSpinBox);
690
691 return d->prefix;
692}
693
694void QDoubleSpinBox::setPrefix(const QString &prefix)
695{
696 Q_D(QDoubleSpinBox);
697
698 d->prefix = prefix;
699 d->updateEdit();
700
701 d->cachedSizeHint = QSize();
702 d->cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about the prefix
703 updateGeometry();
704}
705
706/*!
707 \property QDoubleSpinBox::suffix
708 \brief the suffix of the spin box
709
710 The suffix is appended to the end of the displayed value. Typical
711 use is to display a unit of measurement or a currency symbol. For
712 example:
713
714 \snippet code/src_gui_widgets_qspinbox.cpp 5
715
716 To turn off the suffix display, set this property to an empty
717 string. The default is no suffix. The suffix is not displayed for
718 the minimum() if specialValueText() is set.
719
720 If no suffix is set, suffix() returns an empty string.
721
722 \sa prefix(), setPrefix(), specialValueText(), setSpecialValueText()
723*/
724
725QString QDoubleSpinBox::suffix() const
726{
727 Q_D(const QDoubleSpinBox);
728
729 return d->suffix;
730}
731
732void QDoubleSpinBox::setSuffix(const QString &suffix)
733{
734 Q_D(QDoubleSpinBox);
735
736 d->suffix = suffix;
737 d->updateEdit();
738
739 d->cachedSizeHint = QSize();
740 updateGeometry();
741}
742
743/*!
744 \property QDoubleSpinBox::cleanText
745
746 \brief the text of the spin box excluding any prefix, suffix,
747 or leading or trailing whitespace.
748
749 \sa text, QDoubleSpinBox::prefix, QDoubleSpinBox::suffix
750*/
751
752QString QDoubleSpinBox::cleanText() const
753{
754 Q_D(const QDoubleSpinBox);
755
756 return d->stripped(d->edit->displayText());
757}
758
759/*!
760 \property QDoubleSpinBox::singleStep
761 \brief the step value
762
763 When the user uses the arrows to change the spin box's value the
764 value will be incremented/decremented by the amount of the
765 singleStep. The default value is 1.0. Setting a singleStep value
766 of less than 0 does nothing.
767*/
768double QDoubleSpinBox::singleStep() const
769{
770 Q_D(const QDoubleSpinBox);
771
772 return d->singleStep.toDouble();
773}
774
775void QDoubleSpinBox::setSingleStep(double value)
776{
777 Q_D(QDoubleSpinBox);
778
779 if (value >= 0) {
780 d->singleStep = value;
781 d->updateEdit();
782 }
783}
784
785/*!
786 \property QDoubleSpinBox::minimum
787
788 \brief the minimum value of the spin box
789
790 When setting this property the \l maximum is adjusted
791 if necessary to ensure that the range remains valid.
792
793 The default minimum value is 0.0.
794
795 Note: The minimum value will be rounded to match the decimals
796 property.
797
798 \sa decimals, setRange(), specialValueText
799*/
800
801double QDoubleSpinBox::minimum() const
802{
803 Q_D(const QDoubleSpinBox);
804
805 return d->minimum.toDouble();
806}
807
808void QDoubleSpinBox::setMinimum(double minimum)
809{
810 Q_D(QDoubleSpinBox);
811 d->actualMin = minimum;
812 const QVariant m(d->round(minimum));
813 d->setRange(m, (QDoubleSpinBoxPrivate::variantCompare(d->maximum, m) > 0 ? d->maximum : m));
814}
815
816/*!
817 \property QDoubleSpinBox::maximum
818
819 \brief the maximum value of the spin box
820
821 When setting this property the \l minimum is adjusted
822 if necessary, to ensure that the range remains valid.
823
824 The default maximum value is 99.99.
825
826 Note: The maximum value will be rounded to match the decimals
827 property.
828
829 \sa decimals, setRange()
830*/
831
832double QDoubleSpinBox::maximum() const
833{
834 Q_D(const QDoubleSpinBox);
835
836 return d->maximum.toDouble();
837}
838
839void QDoubleSpinBox::setMaximum(double maximum)
840{
841 Q_D(QDoubleSpinBox);
842 d->actualMax = maximum;
843 const QVariant m(d->round(maximum));
844 d->setRange((QDoubleSpinBoxPrivate::variantCompare(d->minimum, m) < 0 ? d->minimum : m), m);
845}
846
847/*!
848 Convenience function to set the \a minimum and \a maximum values
849 with a single function call.
850
851 Note: The maximum and minimum values will be rounded to match the
852 decimals property.
853
854 \snippet code/src_gui_widgets_qspinbox.cpp 6
855 is equivalent to:
856 \snippet code/src_gui_widgets_qspinbox.cpp 7
857
858 \sa minimum, maximum
859*/
860
861void QDoubleSpinBox::setRange(double minimum, double maximum)
862{
863 Q_D(QDoubleSpinBox);
864 d->actualMin = minimum;
865 d->actualMax = maximum;
866 d->setRange(QVariant(d->round(minimum)), QVariant(d->round(maximum)));
867}
868
869/*!
870 Sets the step type for the spin box to \a stepType, which is single
871 step or adaptive decimal step.
872
873 Adaptive decimal step means that the step size will continuously be
874 adjusted to one power of ten below the current \l value. So when
875 the value is 1100, the step is set to 100, so stepping up once
876 increases it to 1200. For 1200 stepping up takes it to 1300. For
877 negative values, stepping down from -1100 goes to -1200.
878
879 It also works for any decimal values, 0.041 is increased to 0.042
880 by stepping once.
881
882 Step direction is taken into account to handle edges cases, so
883 that stepping down from 100 takes the value to 99 instead of 90.
884 Thus a step up followed by a step down -- or vice versa -- always
885 lands on the starting value; 99 -> 100 -> 99.
886
887 Setting this will cause the spin box to disregard the value of
888 \l singleStep, although it is preserved so that \l singleStep
889 comes into effect if adaptive decimal step is later turned off.
890
891 \since 5.12
892*/
893
894void QDoubleSpinBox::setStepType(StepType stepType)
895{
896 Q_D(QDoubleSpinBox);
897 d->stepType = stepType;
898}
899
900/*!
901 \property QDoubleSpinBox::stepType
902 \brief The step type.
903
904 The step type can be single step or adaptive decimal step.
905*/
906
907QAbstractSpinBox::StepType QDoubleSpinBox::stepType() const
908{
909 Q_D(const QDoubleSpinBox);
910 return d->stepType;
911}
912
913/*!
914 \property QDoubleSpinBox::decimals
915
916 \brief the precision of the spin box, in decimals
917
918 Sets how many decimals the spinbox will use for displaying and
919 interpreting doubles.
920
921 \warning The maximum value for \a decimals is DBL_MAX_10_EXP +
922 DBL_DIG (ie. 323) because of the limitations of the double type.
923
924 Note: The maximum, minimum and value might change as a result of
925 changing this property.
926*/
927
928int QDoubleSpinBox::decimals() const
929{
930 Q_D(const QDoubleSpinBox);
931
932 return d->decimals;
933}
934
935void QDoubleSpinBox::setDecimals(int decimals)
936{
937 Q_D(QDoubleSpinBox);
938 d->decimals = qBound(0, decimals, DBL_MAX_10_EXP + DBL_DIG);
939
940 setRange(d->actualMin, d->actualMax); // make sure values are rounded
941 setValue(value());
942}
943
944/*!
945 This virtual function is used by the spin box whenever it needs to
946 display the given \a value. The default implementation returns a string
947 containing \a value printed using QWidget::locale().toString(\a value,
948 \c u'f', decimals()) and will remove the thousand separator unless
949 setGroupSeparatorShown() is set. Reimplementations may return anything.
950
951 Note: QDoubleSpinBox does not call this function for
952 specialValueText() and that neither prefix() nor suffix() should
953 be included in the return value.
954
955 If you reimplement this, you may also need to reimplement
956 valueFromText().
957
958 \sa valueFromText(), QLocale::groupSeparator()
959*/
960
961
962QString QDoubleSpinBox::textFromValue(double value) const
963{
964 Q_D(const QDoubleSpinBox);
965 QLocale loc = locale();
966 if (d->showGroupSeparator)
967 loc.setNumberOptions(loc.numberOptions() & ~QLocale::OmitGroupSeparator);
968 else
969 loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator);
970 return loc.toString(value, 'f', d->decimals);
971}
972
973/*!
974 This virtual function is used by the spin box whenever it needs to
975 interpret \a text entered by the user as a value.
976
977 Subclasses that need to display spin box values in a non-numeric
978 way need to reimplement this function.
979
980 Note: QDoubleSpinBox handles specialValueText() separately; this
981 function is only concerned with the other values.
982
983 \sa textFromValue(), validate()
984*/
985double QDoubleSpinBox::valueFromText(const QString &text) const
986{
987 Q_D(const QDoubleSpinBox);
988
989 QString copy = text;
990 int pos = d->edit->cursorPosition();
991 QValidator::State state = QValidator::Acceptable;
992 return d->validateAndInterpret(copy, pos, state).toDouble();
993}
994
995/*!
996 \reimp
997*/
998QValidator::State QDoubleSpinBox::validate(QString &text, int &pos) const
999{
1000 Q_D(const QDoubleSpinBox);
1001
1002 QValidator::State state;
1003 d->validateAndInterpret(text, pos, state);
1004 return state;
1005}
1006
1007
1008/*!
1009 \reimp
1010*/
1011void QDoubleSpinBox::fixup(QString &input) const
1012{
1013 input.remove(locale().groupSeparator());
1014}
1015
1016// --- QSpinBoxPrivate ---
1017
1018/*!
1019 \internal
1020 Constructs a QSpinBoxPrivate object
1021*/
1022
1023QSpinBoxPrivate::QSpinBoxPrivate()
1024{
1025 minimum = QVariant((int)0);
1026 maximum = QVariant((int)99);
1027 value = minimum;
1028 displayIntegerBase = 10;
1029 singleStep = QVariant((int)1);
1030 type = QMetaType::Int;
1031}
1032
1033/*!
1034 \internal
1035 \reimp
1036*/
1037
1038void QSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
1039{
1040 Q_Q(QSpinBox);
1041 if (ep != NeverEmit) {
1042 pendingEmit = false;
1043 if (ep == AlwaysEmit || value != old) {
1044 emit q->textChanged(edit->displayText());
1045 emit q->valueChanged(value.toInt());
1046 }
1047 }
1048}
1049
1050/*!
1051 \internal
1052 \reimp
1053*/
1054
1055QString QSpinBoxPrivate::textFromValue(const QVariant &value) const
1056{
1057 Q_Q(const QSpinBox);
1058 return q->textFromValue(value.toInt());
1059}
1060/*!
1061 \internal
1062 \reimp
1063*/
1064
1065QVariant QSpinBoxPrivate::valueFromText(const QString &text) const
1066{
1067 Q_Q(const QSpinBox);
1068
1069 return QVariant(q->valueFromText(text));
1070}
1071
1072
1073/*!
1074 \internal Multi purpose function that parses input, sets state to
1075 the appropriate state and returns the value it will be interpreted
1076 as.
1077*/
1078
1079QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
1080 QValidator::State &state) const
1081{
1082 if (cachedText == input && !input.isEmpty()) {
1083 state = cachedState;
1084 QSBDEBUG() << "cachedText was '" << cachedText << "' state was "
1085 << state << " and value was " << cachedValue;
1086
1087 return cachedValue;
1088 }
1089 const int max = maximum.toInt();
1090 const int min = minimum.toInt();
1091
1092 QString copy = stripped(input, &pos);
1093 QSBDEBUG() << "input" << input << "copy" << copy;
1094 state = QValidator::Acceptable;
1095 int num = min;
1096
1097 if (max != min && (copy.isEmpty()
1098 || (min < 0 && copy == "-"_L1)
1099 || (max >= 0 && copy == "+"_L1))) {
1100 state = QValidator::Intermediate;
1101 QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
1102 } else if (copy.startsWith(u'-') && min >= 0) {
1103 state = QValidator::Invalid; // special-case -0 will be interpreted as 0 and thus not be invalid with a range from 0-100
1104 } else {
1105 bool ok = false;
1106 if (displayIntegerBase != 10) {
1107 num = copy.toInt(&ok, displayIntegerBase);
1108 } else {
1109 num = locale.toInt(copy, &ok);
1110 if (!ok && (max >= 1000 || min <= -1000)) {
1111 const QString sep(locale.groupSeparator());
1112 const QString doubleSep = sep + sep;
1113 if (copy.contains(sep) && !copy.contains(doubleSep)) {
1114 QString copy2 = copy;
1115 copy2.remove(sep);
1116 num = locale.toInt(copy2, &ok);
1117 }
1118 }
1119 }
1120 QSBDEBUG() << __FILE__ << __LINE__<< "num is set to" << num;
1121 if (!ok) {
1122 state = QValidator::Invalid;
1123 } else if (num >= min && num <= max) {
1124 state = QValidator::Acceptable;
1125 } else if (max == min) {
1126 state = QValidator::Invalid;
1127 } else {
1128 if ((num >= 0 && num > max) || (num < 0 && num < min)) {
1129 state = QValidator::Invalid;
1130 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1131 } else {
1132 state = QValidator::Intermediate;
1133 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate";
1134 }
1135 }
1136 }
1137 if (state != QValidator::Acceptable)
1138 num = max > 0 ? min : max;
1139 input = prefix + copy + suffix;
1140 cachedText = input;
1141 cachedState = state;
1142 cachedValue = QVariant((int)num);
1143
1144 QSBDEBUG() << "cachedText is set to '" << cachedText << "' state is set to "
1145 << state << " and value is set to " << cachedValue;
1146 return cachedValue;
1147}
1148
1150{
1151 const int intValue = value.toInt();
1152 const int absValue = qAbs(intValue);
1153
1154 if (absValue < 100)
1155 return 1;
1156
1157 const bool valueNegative = intValue < 0;
1158 const bool stepsNegative = steps < 0;
1159 const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1;
1160
1161 const int log = static_cast<int>(std::log10(absValue - signCompensation)) - 1;
1162 return static_cast<int>(std::pow(10, log));
1163}
1164
1165// --- QDoubleSpinBoxPrivate ---
1166
1167/*!
1168 \internal
1169 Constructs a QSpinBoxPrivate object
1170*/
1171
1172QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate()
1173{
1174 actualMin = 0.0;
1175 actualMax = 99.99;
1176 minimum = QVariant(actualMin);
1177 maximum = QVariant(actualMax);
1178 value = minimum;
1179 singleStep = QVariant(1.0);
1180 decimals = 2;
1181 type = QMetaType::Double;
1182}
1183
1184/*!
1185 \internal
1186 \reimp
1187*/
1188
1189void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
1190{
1191 Q_Q(QDoubleSpinBox);
1192 if (ep != NeverEmit) {
1193 pendingEmit = false;
1194 if (ep == AlwaysEmit || value != old) {
1195 emit q->textChanged(edit->displayText());
1196 emit q->valueChanged(value.toDouble());
1197 }
1198 }
1199}
1200
1201
1202/*!
1203 \internal
1204 \reimp
1205*/
1206QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const
1207{
1208 Q_Q(const QDoubleSpinBox);
1209 return QVariant(q->valueFromText(f));
1210}
1211
1212/*!
1213 \internal
1214 Rounds to a double value that is restricted to decimals.
1215 E.g. // decimals = 2
1216
1217 round(5.555) => 5.56
1218 */
1219
1220double QDoubleSpinBoxPrivate::round(double value) const
1221{
1222 return QString::number(value, 'f', decimals).toDouble();
1223}
1224
1225
1226/*!
1227 \internal Multi purpose function that parses input, sets state to
1228 the appropriate state and returns the value it will be interpreted
1229 as.
1230*/
1231
1232QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
1233 QValidator::State &state) const
1234{
1235 if (cachedText == input && !input.isEmpty()) {
1236 state = cachedState;
1237 QSBDEBUG() << "cachedText was '" << cachedText << "' state was "
1238 << state << " and value was " << cachedValue;
1239 return cachedValue;
1240 }
1241 const double max = maximum.toDouble();
1242 const double min = minimum.toDouble();
1243
1244 QString copy = stripped(input, &pos);
1245 QSBDEBUG() << "input" << input << "copy" << copy;
1246 int len = copy.size();
1247 double num = min;
1248 const bool plus = max >= 0;
1249 const bool minus = min <= 0;
1250
1251 const QString group(locale.groupSeparator());
1252 const uint groupUcs = (group.isEmpty() ? 0 :
1253 (group.size() > 1 && group.at(0).isHighSurrogate()
1254 ? QChar::surrogateToUcs4(group.at(0), group.at(1))
1255 : group.at(0).unicode()));
1256 switch (len) {
1257 case 0:
1258 state = max != min ? QValidator::Intermediate : QValidator::Invalid;
1259 goto end;
1260 case 1:
1261 if (copy.at(0) == locale.decimalPoint()
1262 || (plus && copy.at(0) == u'+')
1263 || (minus && copy.at(0) == u'-')) {
1264 state = QValidator::Intermediate;
1265 goto end;
1266 }
1267 break;
1268 case 2:
1269 if (copy.at(1) == locale.decimalPoint()
1270 && ((plus && copy.at(0) == u'+') || (minus && copy.at(0) == u'-'))) {
1271 state = QValidator::Intermediate;
1272 goto end;
1273 }
1274 break;
1275 default: break;
1276 }
1277
1278 if (groupUcs && copy.startsWith(group)) {
1279 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1280 state = QValidator::Invalid;
1281 goto end;
1282 } else if (len > 1) {
1283 const int dec = copy.indexOf(locale.decimalPoint());
1284 if (dec != -1) {
1285 if (dec + 1 < copy.size() && copy.at(dec + 1) == locale.decimalPoint() && pos == dec + 1) {
1286 copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter
1287 } // should be treated as typing right arrow
1288
1289 if (copy.size() - dec > decimals + 1) {
1290 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1291 state = QValidator::Invalid;
1292 goto end;
1293 }
1294 for (int i = dec + 1; i < copy.size(); ++i) {
1295 if (copy.at(i).isSpace()
1296 || (groupUcs && QStringView{copy}.sliced(i).startsWith(group))) {
1297 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1298 state = QValidator::Invalid;
1299 goto end;
1300 }
1301 }
1302 } else {
1303 const QChar last = copy.back();
1304 const bool groupEnd = groupUcs && copy.endsWith(group);
1305 const QStringView head(copy.constData(), groupEnd ? len - group.size() : len - 1);
1306 const QChar secondLast = head.back();
1307 if ((groupEnd || last.isSpace())
1308 && ((groupUcs && head.endsWith(group)) || secondLast.isSpace())) {
1309 state = QValidator::Invalid;
1310 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1311 goto end;
1312 } else if (last.isSpace() && (!QChar::isSpace(groupUcs) || secondLast.isSpace())) {
1313 state = QValidator::Invalid;
1314 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1315 goto end;
1316 }
1317 }
1318 }
1319
1320 {
1321 bool ok = false;
1322 num = locale.toDouble(copy, &ok);
1323 QSBDEBUG() << __FILE__ << __LINE__ << locale << copy << num << ok;
1324
1325 if (!ok) {
1326 if (QChar::isPrint(groupUcs)) {
1327 if (max < 1000 && min > -1000 && groupUcs && copy.contains(group)) {
1328 state = QValidator::Invalid;
1329 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1330 goto end;
1331 }
1332
1333 const int len = copy.size();
1334 for (int i = 0; i < len - 1;) {
1335 if (groupUcs && QStringView{copy}.sliced(i).startsWith(group)) {
1336 if (QStringView(copy).mid(i + group.size()).startsWith(group)) {
1337 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1338 state = QValidator::Invalid;
1339 goto end;
1340 }
1341 i += group.size();
1342 } else {
1343 i++;
1344 }
1345 }
1346
1347 QString copy2 = copy;
1348 if (groupUcs)
1349 copy2.remove(group);
1350 num = locale.toDouble(copy2, &ok);
1351 QSBDEBUG() << group << num << copy2 << ok;
1352
1353 if (!ok) {
1354 state = QValidator::Invalid;
1355 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1356 goto end;
1357 }
1358 }
1359 }
1360
1361 if (!ok) {
1362 state = QValidator::Invalid;
1363 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1364 } else if (num >= min && num <= max) {
1365 state = QValidator::Acceptable;
1366 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Acceptable";
1367 } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min)
1368 state = QValidator::Invalid;
1369 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1370 } else {
1371 if ((num >= 0 && num > max) || (num < 0 && num < min)) {
1372 state = QValidator::Invalid;
1373 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
1374 } else {
1375 state = QValidator::Intermediate;
1376 QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Intermediate";
1377 }
1378 }
1379 }
1380
1381end:
1382 if (state != QValidator::Acceptable) {
1383 num = max > 0 ? min : max;
1384 }
1385
1386 input = prefix + copy + suffix;
1387 cachedText = input;
1388 cachedState = state;
1389 cachedValue = QVariant(num);
1390 return QVariant(num);
1391}
1392
1393/*
1394 \internal
1395 \reimp
1396*/
1397
1399{
1400 Q_Q(const QDoubleSpinBox);
1401 return q->textFromValue(f.toDouble());
1402}
1403
1405{
1406 const double doubleValue = value.toDouble();
1407 const double minStep = std::pow(10, -decimals);
1408 double absValue = qAbs(doubleValue);
1409
1410 if (absValue < minStep)
1411 return minStep;
1412
1413 const bool valueNegative = doubleValue < 0;
1414 const bool stepsNegative = steps < 0;
1415 if (valueNegative != stepsNegative)
1416 absValue /= 1.01;
1417
1418 const double shift = std::pow(10, 1 - std::floor(std::log10(absValue)));
1419 const double absRounded = round(absValue * shift) / shift;
1420 const double log = floorf(std::log10(absRounded)) - 1;
1421
1422 return std::max(minStep, std::pow(10, log));
1423}
1424
1425/*! \reimp */
1426bool QSpinBox::event(QEvent *event)
1427{
1428 Q_D(QSpinBox);
1429 if (event->type() == QEvent::StyleChange
1430#ifdef Q_OS_MAC
1431 || event->type() == QEvent::MacSizeChange
1432#endif
1433 )
1434 d->setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
1435 return QAbstractSpinBox::event(event);
1436}
1437
1438QT_END_NAMESPACE
1439
1440#include "moc_qspinbox.cpp"
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 QSBDEBUG
Definition qspinbox.cpp:25