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

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