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