Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qspinbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <private/qabstractspinbox_p.h>
6#include <qspinbox.h>
7
8#include <qlocale.h>
9#include <qloggingcategory.h>
10#include <qvalidator.h>
11
12#include <algorithm>
13#include <cmath>
14#include <float.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20Q_STATIC_LOGGING_CATEGORY(lcWidgetSpinBox, "qt.widgets.qspinbox")
21Q_STATIC_LOGGING_CATEGORY(lcWidgetDblSpinBox, "qt.widgets.qdoublespinbox")
22
24{
25 Q_DECLARE_PUBLIC(QSpinBox)
26public:
28 void emitSignals(EmitPolicy ep, const QVariant &) override;
29
30 virtual QVariant valueFromText(const QString &n) const override;
31 virtual QString textFromValue(const QVariant &n) const override;
32 QVariant validateAndInterpret(QString &input, int &pos,
33 QValidator::State &state) const;
34
35 inline void init() {
36 Q_Q(QSpinBox);
37 q->setInputMethodHints(Qt::ImhDigitsOnly);
38 setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
39 }
40
42
43 QVariant calculateAdaptiveDecimalStep(int steps) const override;
44};
45
47{
48 Q_DECLARE_PUBLIC(QDoubleSpinBox)
49public:
51 void emitSignals(EmitPolicy ep, const QVariant &) override;
52
53 virtual QVariant valueFromText(const QString &n) const override;
54 virtual QString textFromValue(const QVariant &n) const override;
55 QVariant validateAndInterpret(QString &input, int &pos,
56 QValidator::State &state) const;
57 QVariant roundValue(const QVariant &val) const override
58 {
59 return round(val.toDouble());
60 }
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 {Spin box widget displaying an integer}
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().\l{QLocale::}{toString()}, but with the thousand
465 separator removed unless setGroupSeparatorShown() is set.
466 Reimplementations may return anything. (See the example in the
467 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().\l{QLocale::}{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 qCDebug(lcWidgetSpinBox) << "cachedText was '" << cachedText << "' state was" << state
1085 << "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 qCDebug(lcWidgetSpinBox) << "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 qCDebug(lcWidgetSpinBox) << "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 qCDebug(lcWidgetSpinBox) << "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 else
1131 state = QValidator::Intermediate;
1132 }
1133 qCDebug(lcWidgetSpinBox) << "state is set to" << state;
1134 }
1135 if (state != QValidator::Acceptable)
1136 num = max > 0 ? min : max;
1137 input = prefix + copy + suffix;
1138 cachedText = input;
1139 cachedState = state;
1140 cachedValue = QVariant((int)num);
1141
1142 qCDebug(lcWidgetSpinBox) << "cachedText is set to '" << cachedText << "' state is set to"
1143 << state << "and value is set to" << cachedValue;
1144 return cachedValue;
1145}
1146
1148{
1149 const int intValue = value.toInt();
1150 const int absValue = qAbs(intValue);
1151
1152 if (absValue < 100)
1153 return 1;
1154
1155 const bool valueNegative = intValue < 0;
1156 const bool stepsNegative = steps < 0;
1157 const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1;
1158
1159 const int log = static_cast<int>(std::log10(absValue - signCompensation)) - 1;
1160 return static_cast<int>(std::pow(10, log));
1161}
1162
1163// --- QDoubleSpinBoxPrivate ---
1164
1165/*!
1166 \internal
1167 Constructs a QSpinBoxPrivate object
1168*/
1169
1170QDoubleSpinBoxPrivate::QDoubleSpinBoxPrivate()
1171{
1172 actualMin = 0.0;
1173 actualMax = 99.99;
1174 minimum = QVariant(actualMin);
1175 maximum = QVariant(actualMax);
1176 value = minimum;
1177 singleStep = QVariant(1.0);
1178 decimals = 2;
1179 type = QMetaType::Double;
1180}
1181
1182/*!
1183 \internal
1184 \reimp
1185*/
1186
1187void QDoubleSpinBoxPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
1188{
1189 Q_Q(QDoubleSpinBox);
1190 if (ep != NeverEmit) {
1191 pendingEmit = false;
1192 if (ep == AlwaysEmit || value != old) {
1193 emit q->textChanged(edit->displayText());
1194 emit q->valueChanged(value.toDouble());
1195 }
1196 }
1197}
1198
1199
1200/*!
1201 \internal
1202 \reimp
1203*/
1204QVariant QDoubleSpinBoxPrivate::valueFromText(const QString &f) const
1205{
1206 Q_Q(const QDoubleSpinBox);
1207 return QVariant(q->valueFromText(f));
1208}
1209
1210/*!
1211 \internal
1212 Rounds to a double value that is restricted to decimals.
1213 E.g. // decimals = 2
1214
1215 round(5.555) => 5.56
1216 */
1217
1218double QDoubleSpinBoxPrivate::round(double value) const
1219{
1220 return QString::number(value, 'f', decimals).toDouble();
1221}
1222
1223
1224/*!
1225 \internal Multi purpose function that parses input, sets state to
1226 the appropriate state and returns the value it will be interpreted
1227 as.
1228*/
1229
1230QVariant QDoubleSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
1231 QValidator::State &state) const
1232{
1233 if (cachedText == input && !input.isEmpty()) {
1234 state = cachedState;
1235 qCDebug(lcWidgetDblSpinBox) << "cachedText was '" << cachedText << "' state was" << state
1236 << "and value was" << cachedValue;
1237 return cachedValue;
1238 }
1239 const double max = maximum.toDouble();
1240 const double min = minimum.toDouble();
1241
1242 QString copy = stripped(input, &pos);
1243 qCDebug(lcWidgetDblSpinBox) << "input" << input << "copy" << copy;
1244 int len = copy.size();
1245 double num = min;
1246 const bool plus = max >= 0;
1247 const bool minus = min <= 0;
1248
1249 const QString group(locale.groupSeparator());
1250 const uint groupUcs = (group.isEmpty() ? 0 :
1251 (group.size() > 1 && group.at(0).isHighSurrogate()
1252 ? QChar::surrogateToUcs4(group.at(0), group.at(1))
1253 : group.at(0).unicode()));
1254 switch (len) {
1255 case 0:
1256 state = max != min ? QValidator::Intermediate : QValidator::Invalid;
1257 goto end;
1258 case 1:
1259 if (copy.at(0) == locale.decimalPoint()
1260 || (plus && copy.at(0) == u'+')
1261 || (minus && copy.at(0) == u'-')) {
1262 state = QValidator::Intermediate;
1263 goto end;
1264 }
1265 break;
1266 case 2:
1267 if (copy.at(1) == locale.decimalPoint()
1268 && ((plus && copy.at(0) == u'+') || (minus && copy.at(0) == u'-'))) {
1269 state = QValidator::Intermediate;
1270 goto end;
1271 }
1272 break;
1273 default: break;
1274 }
1275
1276 if (groupUcs && copy.startsWith(group)) {
1277 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1278 state = QValidator::Invalid;
1279 goto end;
1280 } else if (len > 1) {
1281 const int dec = copy.indexOf(locale.decimalPoint());
1282 if (dec != -1) {
1283 if (dec + 1 < copy.size() && copy.at(dec + 1) == locale.decimalPoint() && pos == dec + 1) {
1284 copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter
1285 } // should be treated as typing right arrow
1286
1287 if (copy.size() - dec > decimals + 1) {
1288 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1289 state = QValidator::Invalid;
1290 goto end;
1291 }
1292 for (int i = dec + 1; i < copy.size(); ++i) {
1293 if (copy.at(i).isSpace()
1294 || (groupUcs && QStringView{copy}.sliced(i).startsWith(group))) {
1295 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1296 state = QValidator::Invalid;
1297 goto end;
1298 }
1299 }
1300 } else {
1301 const QChar last = copy.back();
1302 const bool groupEnd = groupUcs && copy.endsWith(group);
1303 const QStringView head(copy.constData(), groupEnd ? len - group.size() : len - 1);
1304 const QChar secondLast = head.back();
1305 if ((groupEnd || last.isSpace())
1306 && ((groupUcs && head.endsWith(group)) || secondLast.isSpace())) {
1307 state = QValidator::Invalid;
1308 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1309 goto end;
1310 } else if (last.isSpace() && (!QChar::isSpace(groupUcs) || secondLast.isSpace())) {
1311 state = QValidator::Invalid;
1312 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1313 goto end;
1314 }
1315 }
1316 }
1317
1318 {
1319 bool ok = false;
1320 num = locale.toDouble(copy, &ok);
1321 qCDebug(lcWidgetDblSpinBox) << locale << copy << num << ok;
1322
1323 if (!ok) {
1324 if (QChar::isPrint(groupUcs)) {
1325 if (max < 1000 && min > -1000 && groupUcs && copy.contains(group)) {
1326 state = QValidator::Invalid;
1327 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1328 goto end;
1329 }
1330
1331 const int len = copy.size();
1332 for (int i = 0; i < len - 1;) {
1333 if (groupUcs && QStringView{copy}.sliced(i).startsWith(group)) {
1334 if (QStringView(copy).mid(i + group.size()).startsWith(group)) {
1335 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1336 state = QValidator::Invalid;
1337 goto end;
1338 }
1339 i += group.size();
1340 } else {
1341 i++;
1342 }
1343 }
1344
1345 QString copy2 = copy;
1346 if (groupUcs)
1347 copy2.remove(group);
1348 num = locale.toDouble(copy2, &ok);
1349 qCDebug(lcWidgetDblSpinBox) << group << num << copy2 << ok;
1350
1351 if (!ok) {
1352 state = QValidator::Invalid;
1353 qCDebug(lcWidgetDblSpinBox) << "state is set to Invalid";
1354 goto end;
1355 }
1356 }
1357 }
1358
1359 if (!ok) {
1360 state = QValidator::Invalid;
1361 } else if (num >= min && num <= max) {
1362 state = QValidator::Acceptable;
1363 } else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min)
1364 state = QValidator::Invalid;
1365 } else {
1366 if ((num >= 0 && num > max) || (num < 0 && num < min))
1367 state = QValidator::Invalid;
1368 else
1369 state = QValidator::Intermediate;
1370 }
1371 qCDebug(lcWidgetDblSpinBox) << "state is set to" << state;
1372 }
1373
1374end:
1375 if (state != QValidator::Acceptable) {
1376 num = max > 0 ? min : max;
1377 }
1378
1379 input = prefix + copy + suffix;
1380 cachedText = input;
1381 cachedState = state;
1382 cachedValue = QVariant(num);
1383 return QVariant(num);
1384}
1385
1386/*
1387 \internal
1388 \reimp
1389*/
1390
1392{
1393 Q_Q(const QDoubleSpinBox);
1394 return q->textFromValue(f.toDouble());
1395}
1396
1398{
1399 const double doubleValue = value.toDouble();
1400 const double minStep = std::pow(10, -decimals);
1401 double absValue = qAbs(doubleValue);
1402
1403 if (absValue < minStep)
1404 return minStep;
1405
1406 const bool valueNegative = doubleValue < 0;
1407 const bool stepsNegative = steps < 0;
1408 if (valueNegative != stepsNegative)
1409 absValue /= 1.01;
1410
1411 const double shift = std::pow(10, 1 - std::floor(std::log10(absValue)));
1412 const double absRounded = round(absValue * shift) / shift;
1413 const double log = floorf(std::log10(absRounded)) - 1;
1414
1415 return std::max(minStep, std::pow(10, log));
1416}
1417
1418/*! \reimp */
1419bool QSpinBox::event(QEvent *event)
1420{
1421 Q_D(QSpinBox);
1422 if (event->type() == QEvent::StyleChange
1423#ifdef Q_OS_MACOS
1424 || event->type() == QEvent::MacSizeChange
1425#endif
1426 )
1427 d->setLayoutItemMargins(QStyle::SE_SpinBoxLayoutItem);
1428 return QAbstractSpinBox::event(event);
1429}
1430
1431QT_END_NAMESPACE
1432
1433#include "moc_qspinbox.cpp"
QVariant roundValue(const QVariant &val) const override
Definition qspinbox.cpp:57
QVariant calculateAdaptiveDecimalStep(int steps) const override
QVariant validateAndInterpret(QString &input, int &pos, QValidator::State &state) const
void emitSignals(EmitPolicy ep, const QVariant &) override
double round(double input) const
virtual QVariant valueFromText(const QString &n) const override
virtual QString textFromValue(const QVariant &n) const override
void emitSignals(EmitPolicy ep, const QVariant &) override
virtual QVariant valueFromText(const QString &n) const override
QVariant validateAndInterpret(QString &input, int &pos, QValidator::State &state) const
virtual QString textFromValue(const QVariant &n) const override
QVariant calculateAdaptiveDecimalStep(int steps) const override
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)