1// Copyright (C) 2016 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 <qplatformdefs.h>
5#include <private/qabstractspinbox_p.h>
6#include <private/qapplication_p.h>
7#if QT_CONFIG(datetimeparser)
8#include <private/qdatetimeparser_p.h>
9#endif
10#include <private/qlineedit_p.h>
11#include <qabstractspinbox.h>
12
13#include <qapplication.h>
14#include <qstylehints.h>
15#include <qclipboard.h>
16#include <qdatetime.h>
17#include <qevent.h>
18#if QT_CONFIG(menu)
19#include <qmenu.h>
20#endif
21#include <qpainter.h>
22#include <qpalette.h>
23#include <qstylepainter.h>
24#include <qdebug.h>
25#if QT_CONFIG(accessibility)
26# include <qaccessible.h>
27#endif
28
29
30//#define QABSTRACTSPINBOX_QSBDEBUG
31#ifdef QABSTRACTSPINBOX_QSBDEBUG
32# define QASBDEBUG qDebug
33#else
34# define QASBDEBUG if (false) qDebug
35#endif
36
37QT_BEGIN_NAMESPACE
38
39using namespace Qt::StringLiterals;
40
41/*!
42 \class QAbstractSpinBox
43 \brief The QAbstractSpinBox class provides a spinbox and a line edit to
44 display values.
45
46 \ingroup abstractwidgets
47 \inmodule QtWidgets
48
49 The class is designed as a common super class for widgets like
50 QSpinBox, QDoubleSpinBox and QDateTimeEdit
51
52 Here are the main properties of the class:
53
54 \list 1
55
56 \li \l text: The text that is displayed in the QAbstractSpinBox.
57
58 \li \l alignment: The alignment of the text in the QAbstractSpinBox.
59
60 \li \l wrapping: Whether the QAbstractSpinBox wraps from the
61 minimum value to the maximum value and vice versa.
62
63 \endlist
64
65 QAbstractSpinBox provides a virtual stepBy() function that is
66 called whenever the user triggers a step. This function takes an
67 integer value to signify how many steps were taken. E.g. Pressing
68 Qt::Key_Down will trigger a call to stepBy(-1).
69
70 When the user triggers a step whilst holding the Qt::ControlModifier,
71 QAbstractSpinBox steps by 10 instead of making a single step. This
72 step modifier affects wheel events, key events and interaction with
73 the spinbox buttons. Note that on macOS, Control corresponds to the
74 Command key.
75
76 Since Qt 5.12, QStyle::SH_SpinBox_StepModifier can be used to select
77 which Qt::KeyboardModifier increases the step rate. Qt::NoModifier
78 disables this feature.
79
80 QAbstractSpinBox also provide a virtual function stepEnabled() to
81 determine whether stepping up/down is allowed at any point. This
82 function returns a bitset of StepEnabled.
83
84 \sa QAbstractSlider, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
85 {Spin Boxes Example}
86*/
87
88/*!
89 \enum QAbstractSpinBox::StepEnabledFlag
90
91 \value StepNone
92 \value StepUpEnabled
93 \value StepDownEnabled
94*/
95
96/*!
97 \enum QAbstractSpinBox::StepType
98
99 \value DefaultStepType
100 \value AdaptiveDecimalStepType
101*/
102
103/*!
104 \fn void QAbstractSpinBox::editingFinished()
105
106 This signal is emitted editing is finished. This happens when the
107 spinbox loses focus and when enter is pressed.
108*/
109
110/*!
111 Constructs an abstract spinbox with the given \a parent with default
112 \l wrapping, and \l alignment properties.
113*/
114
115QAbstractSpinBox::QAbstractSpinBox(QWidget *parent)
116 : QWidget(*new QAbstractSpinBoxPrivate, parent, { })
117{
118 Q_D(QAbstractSpinBox);
119 d->init();
120}
121
122/*!
123 \internal
124*/
125QAbstractSpinBox::QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent)
126 : QWidget(dd, parent, { })
127{
128 Q_D(QAbstractSpinBox);
129 d->init();
130}
131
132/*!
133 Called when the QAbstractSpinBox is destroyed.
134*/
135
136QAbstractSpinBox::~QAbstractSpinBox()
137{
138}
139
140/*!
141 \enum QAbstractSpinBox::ButtonSymbols
142
143 This enum type describes the symbols that can be displayed on the buttons
144 in a spin box.
145
146 \inlineimage qspinbox-updown.png
147 \inlineimage qspinbox-plusminus.png
148
149 \value UpDownArrows Little arrows in the classic style.
150 \value PlusMinus \b{+} and \b{-} symbols.
151 \value NoButtons Don't display buttons.
152
153 \sa QAbstractSpinBox::buttonSymbols
154*/
155
156/*!
157 \property QAbstractSpinBox::buttonSymbols
158
159 \brief the current button symbol mode
160
161 The possible values can be either \c UpDownArrows or \c PlusMinus.
162 The default is \c UpDownArrows.
163
164 Note that some styles might render PlusMinus and UpDownArrows
165 identically.
166
167 \sa ButtonSymbols
168*/
169
170QAbstractSpinBox::ButtonSymbols QAbstractSpinBox::buttonSymbols() const
171{
172 Q_D(const QAbstractSpinBox);
173 return d->buttonSymbols;
174}
175
176void QAbstractSpinBox::setButtonSymbols(ButtonSymbols buttonSymbols)
177{
178 Q_D(QAbstractSpinBox);
179 if (d->buttonSymbols != buttonSymbols) {
180 d->buttonSymbols = buttonSymbols;
181 d->updateEditFieldGeometry();
182 updateGeometry();
183 update();
184 }
185}
186
187/*!
188 \property QAbstractSpinBox::text
189
190 \brief the spin box's text, including any prefix and suffix
191
192 There is no default text.
193*/
194
195QString QAbstractSpinBox::text() const
196{
197 return lineEdit()->displayText();
198}
199
200
201/*!
202 \property QAbstractSpinBox::specialValueText
203 \brief the special-value text
204
205 If set, the spin box will display this text instead of a numeric
206 value whenever the current value is equal to minimum(). Typical use
207 is to indicate that this choice has a special (default) meaning.
208
209 For example, if your spin box allows the user to choose a scale factor
210 (or zoom level) for displaying an image, and your application is able
211 to automatically choose one that will enable the image to fit completely
212 within the display window, you can set up the spin box like this:
213
214 \snippet widgets/spinboxes/window.cpp 3
215
216 The user will then be able to choose a scale from 1% to 1000%
217 or select "Auto" to leave it up to the application to choose. Your code
218 must then interpret the spin box value of 0 as a request from the user
219 to scale the image to fit inside the window.
220
221 All values are displayed with the prefix and suffix (if set), \e
222 except for the special value, which only shows the special value
223 text. This special text is passed in the QSpinBox::textChanged()
224 signal that passes a QString.
225
226 To turn off the special-value text display, call this function
227 with an empty string. The default is no special-value text, i.e.
228 the numeric value is shown as usual.
229
230 If no special-value text is set, specialValueText() returns an
231 empty string.
232*/
233
234QString QAbstractSpinBox::specialValueText() const
235{
236 Q_D(const QAbstractSpinBox);
237 return d->specialValueText;
238}
239
240void QAbstractSpinBox::setSpecialValueText(const QString &specialValueText)
241{
242 Q_D(QAbstractSpinBox);
243
244 d->specialValueText = specialValueText;
245 d->cachedSizeHint = QSize(); // minimumSizeHint doesn't care about specialValueText
246 d->clearCache();
247 d->updateEdit();
248}
249
250/*!
251 \property QAbstractSpinBox::wrapping
252
253 \brief whether the spin box is circular.
254
255 If wrapping is true stepping up from maximum() value will take you
256 to the minimum() value and vice versa. Wrapping only make sense if
257 you have minimum() and maximum() values set.
258
259 \snippet code/src_gui_widgets_qabstractspinbox.cpp 0
260
261 \sa QSpinBox::minimum(), QSpinBox::maximum()
262*/
263
264bool QAbstractSpinBox::wrapping() const
265{
266 Q_D(const QAbstractSpinBox);
267 return d->wrapping;
268}
269
270void QAbstractSpinBox::setWrapping(bool wrapping)
271{
272 Q_D(QAbstractSpinBox);
273 d->wrapping = wrapping;
274}
275
276
277/*!
278 \property QAbstractSpinBox::readOnly
279 \brief whether the spin box is read only.
280
281 In read-only mode, the user can still copy the text to the
282 clipboard, or drag and drop the text;
283 but cannot edit it.
284
285 The QLineEdit in the QAbstractSpinBox does not show a cursor in
286 read-only mode.
287
288 \sa QLineEdit::readOnly
289*/
290
291bool QAbstractSpinBox::isReadOnly() const
292{
293 Q_D(const QAbstractSpinBox);
294 return d->readOnly;
295}
296
297void QAbstractSpinBox::setReadOnly(bool enable)
298{
299 Q_D(QAbstractSpinBox);
300 d->readOnly = enable;
301 d->edit->setReadOnly(enable);
302 QEvent event(QEvent::ReadOnlyChange);
303 QCoreApplication::sendEvent(receiver: this, event: &event);
304 update();
305}
306
307/*!
308 \property QAbstractSpinBox::keyboardTracking
309 \brief whether keyboard tracking is enabled for the spinbox.
310 \since 4.3
311
312 If keyboard tracking is enabled (the default), the spinbox
313 emits the valueChanged() and textChanged() signals while the
314 new value is being entered from the keyboard.
315
316 E.g. when the user enters the value 600 by typing 6, 0, and 0,
317 the spinbox emits 3 signals with the values 6, 60, and 600
318 respectively.
319
320 If keyboard tracking is disabled, the spinbox doesn't emit the
321 valueChanged() and textChanged() signals while typing. It emits
322 the signals later, when the return key is pressed, when keyboard
323 focus is lost, or when other spinbox functionality is used, e.g.
324 pressing an arrow key.
325*/
326
327bool QAbstractSpinBox::keyboardTracking() const
328{
329 Q_D(const QAbstractSpinBox);
330 return d->keyboardTracking;
331}
332
333void QAbstractSpinBox::setKeyboardTracking(bool enable)
334{
335 Q_D(QAbstractSpinBox);
336 d->keyboardTracking = enable;
337}
338
339/*!
340 \property QAbstractSpinBox::frame
341 \brief whether the spin box draws itself with a frame
342
343 If enabled (the default) the spin box draws itself inside a frame,
344 otherwise the spin box draws itself without any frame.
345*/
346
347bool QAbstractSpinBox::hasFrame() const
348{
349 Q_D(const QAbstractSpinBox);
350 return d->frame;
351}
352
353
354void QAbstractSpinBox::setFrame(bool enable)
355{
356 Q_D(QAbstractSpinBox);
357 d->frame = enable;
358 update();
359 d->updateEditFieldGeometry();
360}
361
362/*!
363 \property QAbstractSpinBox::accelerated
364 \brief whether the spin box will accelerate the frequency of the steps when
365 pressing the step Up/Down buttons.
366 \since 4.2
367
368 If enabled the spin box will increase/decrease the value faster
369 the longer you hold the button down.
370*/
371
372void QAbstractSpinBox::setAccelerated(bool accelerate)
373{
374 Q_D(QAbstractSpinBox);
375 d->accelerate = accelerate;
376
377}
378bool QAbstractSpinBox::isAccelerated() const
379{
380 Q_D(const QAbstractSpinBox);
381 return d->accelerate;
382}
383
384/*!
385 \property QAbstractSpinBox::showGroupSeparator
386 \since 5.3
387
388
389 \brief whether a thousands separator is enabled. By default this
390 property is false.
391*/
392bool QAbstractSpinBox::isGroupSeparatorShown() const
393{
394 Q_D(const QAbstractSpinBox);
395 return d->showGroupSeparator;
396}
397
398void QAbstractSpinBox::setGroupSeparatorShown(bool shown)
399{
400 Q_D(QAbstractSpinBox);
401 if (d->showGroupSeparator == shown)
402 return;
403 d->showGroupSeparator = shown;
404 d->setValue(val: d->value, ep: EmitIfChanged);
405 updateGeometry();
406}
407
408/*!
409 \enum QAbstractSpinBox::CorrectionMode
410
411 This enum type describes the mode the spinbox will use to correct
412 an \l{QValidator::}{Intermediate} value if editing finishes.
413
414 \value CorrectToPreviousValue The spinbox will revert to the last
415 valid value.
416
417 \value CorrectToNearestValue The spinbox will revert to the nearest
418 valid value.
419
420 \sa correctionMode
421*/
422
423/*!
424 \property QAbstractSpinBox::correctionMode
425 \brief the mode to correct an \l{QValidator::}{Intermediate}
426 value if editing finishes
427 \since 4.2
428
429 The default mode is QAbstractSpinBox::CorrectToPreviousValue.
430
431 \sa acceptableInput, validate(), fixup()
432*/
433void QAbstractSpinBox::setCorrectionMode(CorrectionMode correctionMode)
434{
435 Q_D(QAbstractSpinBox);
436 d->correctionMode = correctionMode;
437
438}
439QAbstractSpinBox::CorrectionMode QAbstractSpinBox::correctionMode() const
440{
441 Q_D(const QAbstractSpinBox);
442 return d->correctionMode;
443}
444
445
446/*!
447 \property QAbstractSpinBox::acceptableInput
448 \brief whether the input satisfies the current validation
449 \since 4.2
450
451 \sa validate(), fixup(), correctionMode
452*/
453
454bool QAbstractSpinBox::hasAcceptableInput() const
455{
456 Q_D(const QAbstractSpinBox);
457 return d->edit->hasAcceptableInput();
458}
459
460/*!
461 \property QAbstractSpinBox::alignment
462 \brief the alignment of the spin box
463
464 Possible Values are Qt::AlignLeft, Qt::AlignRight, and Qt::AlignHCenter.
465
466 By default, the alignment is Qt::AlignLeft
467
468 Attempting to set the alignment to an illegal flag combination
469 does nothing.
470
471 \sa Qt::Alignment
472*/
473
474Qt::Alignment QAbstractSpinBox::alignment() const
475{
476 Q_D(const QAbstractSpinBox);
477
478 return (Qt::Alignment)d->edit->alignment();
479}
480
481void QAbstractSpinBox::setAlignment(Qt::Alignment flag)
482{
483 Q_D(QAbstractSpinBox);
484
485 d->edit->setAlignment(flag);
486}
487
488/*!
489 Selects all the text in the spinbox except the prefix and suffix.
490*/
491
492void QAbstractSpinBox::selectAll()
493{
494 Q_D(QAbstractSpinBox);
495
496
497 if (!d->specialValue()) {
498 const int tmp = d->edit->displayText().size() - d->suffix.size();
499 d->edit->setSelection(tmp, -(tmp - d->prefix.size()));
500 } else {
501 d->edit->selectAll();
502 }
503}
504
505/*!
506 Clears the lineedit of all text but prefix and suffix.
507*/
508
509void QAbstractSpinBox::clear()
510{
511 Q_D(QAbstractSpinBox);
512
513 d->edit->setText(d->prefix + d->suffix);
514 d->edit->setCursorPosition(d->prefix.size());
515 d->cleared = true;
516}
517
518/*!
519 Virtual function that determines whether stepping up and down is
520 legal at any given time.
521
522 The up arrow will be painted as disabled unless (stepEnabled() &
523 StepUpEnabled) != 0.
524
525 The default implementation will return (StepUpEnabled|
526 StepDownEnabled) if wrapping is turned on. Else it will return
527 StepDownEnabled if value is > minimum() or'ed with StepUpEnabled if
528 value < maximum().
529
530 If you subclass QAbstractSpinBox you will need to reimplement this function.
531
532 \sa QSpinBox::minimum(), QSpinBox::maximum(), wrapping()
533*/
534
535
536QAbstractSpinBox::StepEnabled QAbstractSpinBox::stepEnabled() const
537{
538 Q_D(const QAbstractSpinBox);
539 if (d->readOnly || d->type == QMetaType::UnknownType)
540 return StepNone;
541 if (d->wrapping)
542 return StepEnabled(StepUpEnabled | StepDownEnabled);
543 StepEnabled ret = StepNone;
544 if (QAbstractSpinBoxPrivate::variantCompare(arg1: d->value, arg2: d->maximum) < 0) {
545 ret |= StepUpEnabled;
546 }
547 if (QAbstractSpinBoxPrivate::variantCompare(arg1: d->value, arg2: d->minimum) > 0) {
548 ret |= StepDownEnabled;
549 }
550 return ret;
551}
552
553/*!
554 This virtual function is called by the QAbstractSpinBox to
555 determine whether \a input is valid. The \a pos parameter indicates
556 the position in the string. Reimplemented in the various
557 subclasses.
558*/
559
560QValidator::State QAbstractSpinBox::validate(QString & /* input */, int & /* pos */) const
561{
562 return QValidator::Acceptable;
563}
564
565/*!
566 This virtual function is called by the QAbstractSpinBox if the
567 \a input is not validated to QValidator::Acceptable when Return is
568 pressed or interpretText() is called. It will try to change the
569 text so it is valid. Reimplemented in the various subclasses.
570*/
571
572void QAbstractSpinBox::fixup(QString & /* input */) const
573{
574}
575
576/*!
577 Steps up by one linestep
578 Calling this slot is analogous to calling stepBy(1);
579 \sa stepBy(), stepDown()
580*/
581
582void QAbstractSpinBox::stepUp()
583{
584 stepBy(steps: 1);
585}
586
587/*!
588 Steps down by one linestep
589 Calling this slot is analogous to calling stepBy(-1);
590 \sa stepBy(), stepUp()
591*/
592
593void QAbstractSpinBox::stepDown()
594{
595 stepBy(steps: -1);
596}
597/*!
598 Virtual function that is called whenever the user triggers a step.
599 The \a steps parameter indicates how many steps were taken.
600 For example, pressing \c Qt::Key_Down will trigger a call to \c stepBy(-1),
601 whereas pressing \c Qt::Key_PageUp will trigger a call to \c stepBy(10).
602
603 If you subclass \c QAbstractSpinBox you must reimplement this
604 function. Note that this function is called even if the resulting
605 value will be outside the bounds of minimum and maximum. It's this
606 function's job to handle these situations.
607
608 \sa stepUp(), stepDown(), keyPressEvent()
609*/
610
611void QAbstractSpinBox::stepBy(int steps)
612{
613 Q_D(QAbstractSpinBox);
614
615 const QVariant old = d->value;
616 QString tmp = d->edit->displayText();
617 int cursorPos = d->edit->cursorPosition();
618 bool dontstep = false;
619 EmitPolicy e = EmitIfChanged;
620 if (d->pendingEmit) {
621 dontstep = validate(tmp, cursorPos) != QValidator::Acceptable;
622 d->cleared = false;
623 d->interpret(ep: NeverEmit);
624 if (d->value != old)
625 e = AlwaysEmit;
626 }
627 if (!dontstep) {
628 QVariant singleStep;
629 switch (d->stepType) {
630 case QAbstractSpinBox::StepType::AdaptiveDecimalStepType:
631 singleStep = d->calculateAdaptiveDecimalStep(steps);
632 break;
633 default:
634 singleStep = d->singleStep;
635 }
636 d->setValue(val: d->bound(val: d->value + (singleStep * steps), old, steps), ep: e);
637 } else if (e == AlwaysEmit) {
638 d->emitSignals(ep: e, old);
639 }
640 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_SelectOnStep, opt: nullptr, widget: this, returnData: nullptr))
641 selectAll();
642}
643
644/*!
645 This function returns a pointer to the line edit of the spin box.
646*/
647
648QLineEdit *QAbstractSpinBox::lineEdit() const
649{
650 Q_D(const QAbstractSpinBox);
651
652 return d->edit;
653}
654
655
656/*!
657 \fn void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
658
659 Sets the line edit of the spinbox to be \a lineEdit instead of the
660 current line edit widget. \a lineEdit cannot be \nullptr.
661
662 QAbstractSpinBox takes ownership of the new lineEdit
663
664 If QLineEdit::validator() for the \a lineEdit returns \nullptr, the internal
665 validator of the spinbox will be set on the line edit.
666*/
667
668void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
669{
670 Q_D(QAbstractSpinBox);
671
672 if (!lineEdit) {
673 Q_ASSERT(lineEdit);
674 return;
675 }
676
677 if (lineEdit == d->edit)
678 return;
679
680 delete d->edit;
681 d->edit = lineEdit;
682 setProperty(name: "_q_spinbox_lineedit", value: QVariant::fromValue<QWidget *>(value: d->edit));
683 if (!d->edit->validator())
684 d->edit->setValidator(d->validator);
685
686 if (d->edit->parent() != this)
687 d->edit->setParent(this);
688
689 d->edit->setFrame(!style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this));
690 d->edit->setFocusProxy(this);
691 d->edit->setAcceptDrops(false);
692
693 if (d->type != QMetaType::UnknownType) {
694 connect(sender: d->edit, SIGNAL(textChanged(QString)),
695 receiver: this, SLOT(_q_editorTextChanged(QString)));
696 connect(sender: d->edit, SIGNAL(cursorPositionChanged(int,int)),
697 receiver: this, SLOT(_q_editorCursorPositionChanged(int,int)));
698 connect(sender: d->edit, SIGNAL(cursorPositionChanged(int,int)),
699 receiver: this, SLOT(updateMicroFocus()));
700 connect(sender: d->edit->d_func()->control, SIGNAL(updateMicroFocus()),
701 receiver: this, SLOT(updateMicroFocus()));
702 }
703 d->updateEditFieldGeometry();
704 d->edit->setContextMenuPolicy(Qt::NoContextMenu);
705 d->edit->d_func()->control->setAccessibleObject(this);
706
707 if (isVisible())
708 d->edit->show();
709 if (isVisible())
710 d->updateEdit();
711}
712
713
714/*!
715 This function interprets the text of the spin box. If the value
716 has changed since last interpretation it will emit signals.
717*/
718
719void QAbstractSpinBox::interpretText()
720{
721 Q_D(QAbstractSpinBox);
722 d->interpret(ep: EmitIfChanged);
723}
724
725/*
726 Reimplemented in 4.6, so be careful.
727 */
728/*!
729 \reimp
730*/
731QVariant QAbstractSpinBox::inputMethodQuery(Qt::InputMethodQuery query) const
732{
733 Q_D(const QAbstractSpinBox);
734 const QVariant lineEditValue = d->edit->inputMethodQuery(query);
735 switch (query) {
736 case Qt::ImHints:
737 if (const int hints = inputMethodHints())
738 return QVariant(hints | lineEditValue.toInt());
739 break;
740 default:
741 break;
742 }
743 return lineEditValue;
744}
745
746/*!
747 \reimp
748*/
749
750bool QAbstractSpinBox::event(QEvent *event)
751{
752 Q_D(QAbstractSpinBox);
753 switch (event->type()) {
754 case QEvent::FontChange:
755 case QEvent::StyleChange:
756 d->cachedSizeHint = d->cachedMinimumSizeHint = QSize();
757 break;
758 case QEvent::ApplicationLayoutDirectionChange:
759 case QEvent::LayoutDirectionChange:
760 d->updateEditFieldGeometry();
761 break;
762 case QEvent::HoverEnter:
763 case QEvent::HoverLeave:
764 case QEvent::HoverMove:
765 d->updateHoverControl(pos: static_cast<const QHoverEvent *>(event)->position().toPoint());
766 break;
767 case QEvent::ShortcutOverride:
768 if (d->edit->event(event))
769 return true;
770 break;
771#ifdef QT_KEYPAD_NAVIGATION
772 case QEvent::EnterEditFocus:
773 case QEvent::LeaveEditFocus:
774 if (QApplicationPrivate::keypadNavigationEnabled()) {
775 const bool b = d->edit->event(event);
776 d->edit->setSelection(d->edit->displayText().size() - d->suffix.size(),0);
777 if (event->type() == QEvent::LeaveEditFocus)
778 emit editingFinished();
779 if (b)
780 return true;
781 }
782 break;
783#endif
784 case QEvent::InputMethod:
785 return d->edit->event(event);
786 default:
787 break;
788 }
789 return QWidget::event(event);
790}
791
792/*!
793 \reimp
794*/
795
796void QAbstractSpinBox::showEvent(QShowEvent *)
797{
798 Q_D(QAbstractSpinBox);
799 d->reset();
800
801 if (d->ignoreUpdateEdit) {
802 d->ignoreUpdateEdit = false;
803 } else {
804 d->updateEdit();
805 }
806}
807
808/*!
809 \reimp
810*/
811
812void QAbstractSpinBox::changeEvent(QEvent *event)
813{
814 Q_D(QAbstractSpinBox);
815
816 switch (event->type()) {
817 case QEvent::StyleChange:
818 d->spinClickTimerInterval = style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatRate, opt: nullptr, widget: this);
819 d->spinClickThresholdTimerInterval =
820 style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatThreshold, opt: nullptr, widget: this);
821 if (d->edit)
822 d->edit->setFrame(!style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this));
823 d->stepModifier = static_cast<Qt::KeyboardModifier>(style()->styleHint(stylehint: QStyle::SH_SpinBox_StepModifier, opt: nullptr, widget: this));
824 d->reset();
825 d->updateEditFieldGeometry();
826 break;
827 case QEvent::LocaleChange:
828 d->updateEdit();
829 break;
830 case QEvent::EnabledChange:
831 if (!isEnabled()) {
832 d->reset();
833 }
834 break;
835 case QEvent::ActivationChange:
836 if (!isActiveWindow()){
837 d->reset();
838 if (d->pendingEmit) // pendingEmit can be true even if it hasn't changed.
839 d->interpret(ep: EmitIfChanged); // E.g. 10 to 10.0
840 }
841 break;
842 default:
843 break;
844 }
845 QWidget::changeEvent(event);
846}
847
848/*!
849 \reimp
850*/
851
852void QAbstractSpinBox::resizeEvent(QResizeEvent *event)
853{
854 Q_D(QAbstractSpinBox);
855 QWidget::resizeEvent(event);
856
857 d->updateEditFieldGeometry();
858 update();
859}
860
861/*!
862 \reimp
863*/
864
865QSize QAbstractSpinBox::sizeHint() const
866{
867 Q_D(const QAbstractSpinBox);
868 if (d->cachedSizeHint.isEmpty()) {
869 ensurePolished();
870
871 const QFontMetrics fm(fontMetrics());
872 int h = d->edit->sizeHint().height();
873 int w = 0;
874 QString s;
875 QString fixedContent = d->prefix + d->suffix + u' ';
876 s = d->textFromValue(n: d->minimum);
877 s.truncate(pos: 18);
878 s += fixedContent;
879 w = qMax(a: w, b: fm.horizontalAdvance(s));
880 s = d->textFromValue(n: d->maximum);
881 s.truncate(pos: 18);
882 s += fixedContent;
883 w = qMax(a: w, b: fm.horizontalAdvance(s));
884
885 if (d->specialValueText.size()) {
886 s = d->specialValueText;
887 w = qMax(a: w, b: fm.horizontalAdvance(s));
888 }
889 w += 2; // cursor blinking space
890
891 QStyleOptionSpinBox opt;
892 initStyleOption(option: &opt);
893 QSize hint(w, h);
894 d->cachedSizeHint = style()->sizeFromContents(ct: QStyle::CT_SpinBox, opt: &opt, contentsSize: hint, w: this);
895 }
896 return d->cachedSizeHint;
897}
898
899/*!
900 \reimp
901*/
902
903QSize QAbstractSpinBox::minimumSizeHint() const
904{
905 Q_D(const QAbstractSpinBox);
906 if (d->cachedMinimumSizeHint.isEmpty()) {
907 //Use the prefix and range to calculate the minimumSizeHint
908 ensurePolished();
909
910 const QFontMetrics fm(fontMetrics());
911 int h = d->edit->minimumSizeHint().height();
912 int w = 0;
913
914 QString s;
915 QString fixedContent = d->prefix + u' ';
916 s = d->textFromValue(n: d->minimum);
917 s.truncate(pos: 18);
918 s += fixedContent;
919 w = qMax(a: w, b: fm.horizontalAdvance(s));
920 s = d->textFromValue(n: d->maximum);
921 s.truncate(pos: 18);
922 s += fixedContent;
923 w = qMax(a: w, b: fm.horizontalAdvance(s));
924
925 if (d->specialValueText.size()) {
926 s = d->specialValueText;
927 w = qMax(a: w, b: fm.horizontalAdvance(s));
928 }
929 w += 2; // cursor blinking space
930
931 QStyleOptionSpinBox opt;
932 initStyleOption(option: &opt);
933 QSize hint(w, h);
934
935 d->cachedMinimumSizeHint = style()->sizeFromContents(ct: QStyle::CT_SpinBox, opt: &opt, contentsSize: hint, w: this);
936 }
937 return d->cachedMinimumSizeHint;
938}
939
940/*!
941 \reimp
942*/
943
944void QAbstractSpinBox::paintEvent(QPaintEvent *)
945{
946 QStyleOptionSpinBox opt;
947 initStyleOption(option: &opt);
948 QStylePainter p(this);
949 p.drawComplexControl(cc: QStyle::CC_SpinBox, opt);
950}
951
952/*!
953 \reimp
954
955 This function handles keyboard input.
956
957 The following keys are handled specifically:
958 \table
959 \row \li Enter/Return
960 \li This will reinterpret the text and emit a signal even if the value has not changed
961 since last time a signal was emitted.
962 \row \li Up
963 \li This will invoke stepBy(1)
964 \row \li Down
965 \li This will invoke stepBy(-1)
966 \row \li Page up
967 \li This will invoke stepBy(10)
968 \row \li Page down
969 \li This will invoke stepBy(-10)
970 \endtable
971
972 \sa stepBy()
973*/
974
975
976void QAbstractSpinBox::keyPressEvent(QKeyEvent *event)
977{
978 Q_D(QAbstractSpinBox);
979
980 d->keyboardModifiers = event->modifiers();
981
982 if (!event->text().isEmpty() && d->edit->cursorPosition() < d->prefix.size())
983 d->edit->setCursorPosition(d->prefix.size());
984
985 int steps = 1;
986 bool isPgUpOrDown = false;
987 switch (event->key()) {
988 case Qt::Key_PageUp:
989 case Qt::Key_PageDown:
990 steps *= 10;
991 isPgUpOrDown = true;
992 Q_FALLTHROUGH();
993 case Qt::Key_Up:
994 case Qt::Key_Down: {
995#ifdef QT_KEYPAD_NAVIGATION
996 if (QApplicationPrivate::keypadNavigationEnabled()) {
997 // Reserve up/down for nav - use left/right for edit.
998 if (!hasEditFocus() && (event->key() == Qt::Key_Up
999 || event->key() == Qt::Key_Down)) {
1000 event->ignore();
1001 return;
1002 }
1003 }
1004#endif
1005 event->accept();
1006 const bool up = (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_Up);
1007 if (!(stepEnabled() & (up ? StepUpEnabled : StepDownEnabled)))
1008 return;
1009 if (!isPgUpOrDown && (event->modifiers() & d->stepModifier))
1010 steps *= 10;
1011 if (!up)
1012 steps *= -1;
1013 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_AnimateButton, opt: nullptr, widget: this)) {
1014 d->buttonState = (Keyboard | (up ? Up : Down));
1015 }
1016 if (d->spinClickTimerId == -1)
1017 stepBy(steps);
1018 if (event->isAutoRepeat() && !isPgUpOrDown) {
1019 if (d->spinClickThresholdTimerId == -1 && d->spinClickTimerId == -1) {
1020 d->updateState(up, fromKeyboard: true);
1021 }
1022 }
1023#if QT_CONFIG(accessibility)
1024 QAccessibleValueChangeEvent event(this, d->value);
1025 QAccessible::updateAccessibility(event: &event);
1026#endif
1027 return;
1028 }
1029#ifdef QT_KEYPAD_NAVIGATION
1030 case Qt::Key_Left:
1031 case Qt::Key_Right:
1032 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1033 event->ignore();
1034 return;
1035 }
1036 break;
1037 case Qt::Key_Back:
1038 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1039 event->ignore();
1040 return;
1041 }
1042 break;
1043#endif
1044 case Qt::Key_Enter:
1045 case Qt::Key_Return:
1046 d->edit->d_func()->control->clearUndo();
1047 d->interpret(ep: d->keyboardTracking ? AlwaysEmit : EmitIfChanged);
1048 selectAll();
1049 event->ignore();
1050 emit editingFinished();
1051 emit d->edit->returnPressed();
1052 return;
1053
1054#ifdef QT_KEYPAD_NAVIGATION
1055 case Qt::Key_Select:
1056 if (QApplicationPrivate::keypadNavigationEnabled()) {
1057 // Toggles between left/right moving cursor and inc/dec.
1058 setEditFocus(!hasEditFocus());
1059 }
1060 return;
1061#endif
1062
1063 case Qt::Key_U:
1064 if (event->modifiers() & Qt::ControlModifier
1065 && QGuiApplication::platformName() == "xcb"_L1) { // only X11
1066 event->accept();
1067 if (!isReadOnly())
1068 clear();
1069 return;
1070 }
1071 break;
1072
1073 case Qt::Key_End:
1074 case Qt::Key_Home:
1075 if (event->modifiers() & Qt::ShiftModifier) {
1076 int currentPos = d->edit->cursorPosition();
1077 const QString text = d->edit->displayText();
1078 if (event->key() == Qt::Key_End) {
1079 if ((currentPos == 0 && !d->prefix.isEmpty()) || text.size() - d->suffix.size() <= currentPos) {
1080 break; // let lineedit handle this
1081 } else {
1082 d->edit->setSelection(currentPos, text.size() - d->suffix.size() - currentPos);
1083 }
1084 } else {
1085 if ((currentPos == text.size() && !d->suffix.isEmpty()) || currentPos <= d->prefix.size()) {
1086 break; // let lineedit handle this
1087 } else {
1088 d->edit->setSelection(currentPos, d->prefix.size() - currentPos);
1089 }
1090 }
1091 event->accept();
1092 return;
1093 }
1094 break;
1095
1096 default:
1097#ifndef QT_NO_SHORTCUT
1098 if (event == QKeySequence::SelectAll) {
1099 selectAll();
1100 event->accept();
1101 return;
1102 }
1103#endif
1104 break;
1105 }
1106
1107 d->edit->event(event);
1108 if (!d->edit->text().isEmpty())
1109 d->cleared = false;
1110 if (!isVisible())
1111 d->ignoreUpdateEdit = true;
1112}
1113
1114/*!
1115 \reimp
1116*/
1117
1118void QAbstractSpinBox::keyReleaseEvent(QKeyEvent *event)
1119{
1120 Q_D(QAbstractSpinBox);
1121
1122 d->keyboardModifiers = event->modifiers();
1123 if (d->buttonState & Keyboard && !event->isAutoRepeat()) {
1124 d->reset();
1125 } else {
1126 d->edit->event(event);
1127 }
1128}
1129
1130/*!
1131 \reimp
1132*/
1133
1134#if QT_CONFIG(wheelevent)
1135void QAbstractSpinBox::wheelEvent(QWheelEvent *event)
1136{
1137 Q_D(QAbstractSpinBox);
1138#ifdef Q_OS_MACOS
1139 // If the event comes from a real mouse wheel, rather than a track pad
1140 // (Qt::MouseEventSynthesizedBySystem), the shift modifier changes the
1141 // scroll orientation to horizontal.
1142 // Convert horizontal events back to vertical whilst shift is held.
1143 if ((event->modifiers() & Qt::ShiftModifier)
1144 && event->source() == Qt::MouseEventNotSynthesized) {
1145 d->wheelDeltaRemainder += event->angleDelta().x();
1146 } else {
1147 d->wheelDeltaRemainder += event->angleDelta().y();
1148 }
1149#else
1150 d->wheelDeltaRemainder += event->angleDelta().y();
1151#endif
1152 const int steps = d->wheelDeltaRemainder / 120;
1153 d->wheelDeltaRemainder -= steps * 120;
1154 if (stepEnabled() & (steps > 0 ? StepUpEnabled : StepDownEnabled))
1155 stepBy(steps: event->modifiers() & d->stepModifier ? steps * 10 : steps);
1156 event->accept();
1157}
1158#endif
1159
1160
1161/*!
1162 \reimp
1163*/
1164void QAbstractSpinBox::focusInEvent(QFocusEvent *event)
1165{
1166 Q_D(QAbstractSpinBox);
1167
1168 d->edit->event(event);
1169 if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1170 selectAll();
1171 }
1172 QWidget::focusInEvent(event);
1173}
1174
1175/*!
1176 \reimp
1177*/
1178
1179void QAbstractSpinBox::focusOutEvent(QFocusEvent *event)
1180{
1181 Q_D(QAbstractSpinBox);
1182
1183 if (d->pendingEmit)
1184 d->interpret(ep: EmitIfChanged);
1185
1186 d->reset();
1187 d->edit->event(event);
1188 d->updateEdit();
1189 QWidget::focusOutEvent(event);
1190
1191#ifdef QT_KEYPAD_NAVIGATION
1192 // editingFinished() is already emitted on LeaveEditFocus
1193 if (!QApplicationPrivate::keypadNavigationEnabled())
1194#endif
1195 emit editingFinished();
1196}
1197
1198/*!
1199 \reimp
1200*/
1201
1202void QAbstractSpinBox::closeEvent(QCloseEvent *event)
1203{
1204 Q_D(QAbstractSpinBox);
1205
1206 d->reset();
1207 if (d->pendingEmit)
1208 d->interpret(ep: EmitIfChanged);
1209 QWidget::closeEvent(event);
1210}
1211
1212/*!
1213 \reimp
1214*/
1215
1216void QAbstractSpinBox::hideEvent(QHideEvent *event)
1217{
1218 Q_D(QAbstractSpinBox);
1219 d->reset();
1220 if (d->pendingEmit)
1221 d->interpret(ep: EmitIfChanged);
1222 QWidget::hideEvent(event);
1223}
1224
1225
1226/*!
1227 \reimp
1228*/
1229
1230void QAbstractSpinBox::timerEvent(QTimerEvent *event)
1231{
1232 Q_D(QAbstractSpinBox);
1233
1234 bool doStep = false;
1235 if (event->timerId() == d->spinClickThresholdTimerId) {
1236 killTimer(id: d->spinClickThresholdTimerId);
1237 d->spinClickThresholdTimerId = -1;
1238 d->effectiveSpinRepeatRate = d->buttonState & Keyboard
1239 ? QGuiApplication::styleHints()->keyboardAutoRepeatRateF()
1240 : d->spinClickTimerInterval;
1241 d->spinClickTimerId = startTimer(interval: d->effectiveSpinRepeatRate);
1242 doStep = true;
1243 } else if (event->timerId() == d->spinClickTimerId) {
1244 if (d->accelerate) {
1245 d->acceleration = d->acceleration + (int)(d->effectiveSpinRepeatRate * 0.05);
1246 if (d->effectiveSpinRepeatRate - d->acceleration >= 10) {
1247 killTimer(id: d->spinClickTimerId);
1248 d->spinClickTimerId = startTimer(interval: d->effectiveSpinRepeatRate - d->acceleration);
1249 }
1250 }
1251 doStep = true;
1252 }
1253
1254 if (doStep) {
1255 const bool increaseStepRate = d->keyboardModifiers & d->stepModifier;
1256 const StepEnabled st = stepEnabled();
1257 if (d->buttonState & Up) {
1258 if (!(st & StepUpEnabled)) {
1259 d->reset();
1260 } else {
1261 stepBy(steps: increaseStepRate ? 10 : 1);
1262 }
1263 } else if (d->buttonState & Down) {
1264 if (!(st & StepDownEnabled)) {
1265 d->reset();
1266 } else {
1267 stepBy(steps: increaseStepRate ? -10 : -1);
1268 }
1269 }
1270 return;
1271 }
1272 QWidget::timerEvent(event);
1273 return;
1274}
1275
1276/*!
1277 \reimp
1278*/
1279
1280#if QT_CONFIG(contextmenu)
1281void QAbstractSpinBox::contextMenuEvent(QContextMenuEvent *event)
1282{
1283 Q_D(QAbstractSpinBox);
1284
1285 QPointer<QMenu> menu = d->edit->createStandardContextMenu();
1286 if (!menu)
1287 return;
1288
1289 d->reset();
1290
1291 QAction *selAll = new QAction(tr(s: "&Select All"), menu);
1292#if QT_CONFIG(shortcut)
1293 selAll->setShortcut(QKeySequence::SelectAll);
1294#endif
1295 menu->insertAction(before: d->edit->d_func()->selectAllAction,
1296 action: selAll);
1297 menu->removeAction(action: d->edit->d_func()->selectAllAction);
1298 menu->addSeparator();
1299 const uint se = stepEnabled();
1300 QAction *up = menu->addAction(text: tr(s: "&Step up"));
1301 up->setEnabled(se & StepUpEnabled);
1302 QAction *down = menu->addAction(text: tr(s: "Step &down"));
1303 down->setEnabled(se & StepDownEnabled);
1304 menu->addSeparator();
1305
1306 const QPointer<QAbstractSpinBox> that = this;
1307 const QPoint pos = (event->reason() == QContextMenuEvent::Mouse)
1308 ? event->globalPos() : mapToGlobal(QPoint(event->pos().x(), 0)) + QPoint(width() / 2, height() / 2);
1309 const QAction *action = menu->exec(pos);
1310 delete static_cast<QMenu *>(menu);
1311 if (that && action) {
1312 if (action == up) {
1313 stepBy(steps: 1);
1314 } else if (action == down) {
1315 stepBy(steps: -1);
1316 } else if (action == selAll) {
1317 selectAll();
1318 }
1319 }
1320 event->accept();
1321}
1322#endif // QT_CONFIG(contextmenu)
1323
1324/*!
1325 \reimp
1326*/
1327
1328void QAbstractSpinBox::mouseMoveEvent(QMouseEvent *event)
1329{
1330 Q_D(QAbstractSpinBox);
1331
1332 d->keyboardModifiers = event->modifiers();
1333 d->updateHoverControl(pos: event->position().toPoint());
1334
1335 // If we have a timer ID, update the state
1336 if (d->spinClickTimerId != -1 && d->buttonSymbols != NoButtons) {
1337 const StepEnabled se = stepEnabled();
1338 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp)
1339 d->updateState(up: true);
1340 else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown)
1341 d->updateState(up: false);
1342 else
1343 d->reset();
1344 event->accept();
1345 }
1346}
1347
1348/*!
1349 \reimp
1350*/
1351
1352void QAbstractSpinBox::mousePressEvent(QMouseEvent *event)
1353{
1354 Q_D(QAbstractSpinBox);
1355
1356 d->keyboardModifiers = event->modifiers();
1357 if (event->button() != Qt::LeftButton || d->buttonState != None) {
1358 return;
1359 }
1360
1361 d->updateHoverControl(pos: event->position().toPoint());
1362 event->accept();
1363
1364 const StepEnabled se = (d->buttonSymbols == NoButtons) ? StepEnabled(StepNone) : stepEnabled();
1365 if ((se & StepUpEnabled) && d->hoverControl == QStyle::SC_SpinBoxUp) {
1366 d->updateState(up: true);
1367 } else if ((se & StepDownEnabled) && d->hoverControl == QStyle::SC_SpinBoxDown) {
1368 d->updateState(up: false);
1369 } else {
1370 event->ignore();
1371 }
1372}
1373
1374/*!
1375 \reimp
1376*/
1377void QAbstractSpinBox::mouseReleaseEvent(QMouseEvent *event)
1378{
1379 Q_D(QAbstractSpinBox);
1380
1381 d->keyboardModifiers = event->modifiers();
1382 if ((d->buttonState & Mouse) != 0)
1383 d->reset();
1384 event->accept();
1385}
1386
1387// --- QAbstractSpinBoxPrivate ---
1388
1389/*!
1390 \internal
1391 Constructs a QAbstractSpinBoxPrivate object
1392*/
1393
1394QAbstractSpinBoxPrivate::QAbstractSpinBoxPrivate()
1395 : pendingEmit(false), readOnly(false), wrapping(false),
1396 ignoreCursorPositionChanged(false), frame(true), accelerate(false), keyboardTracking(true),
1397 cleared(false), ignoreUpdateEdit(false), showGroupSeparator(false)
1398{
1399}
1400
1401/*
1402 \internal
1403 Called when the QAbstractSpinBoxPrivate is destroyed
1404*/
1405QAbstractSpinBoxPrivate::~QAbstractSpinBoxPrivate()
1406{
1407}
1408
1409/*!
1410 \internal
1411 Updates the old and new hover control. Does nothing if the hover
1412 control has not changed.
1413*/
1414bool QAbstractSpinBoxPrivate::updateHoverControl(const QPoint &pos)
1415{
1416 Q_Q(QAbstractSpinBox);
1417 QRect lastHoverRect = hoverRect;
1418 QStyle::SubControl lastHoverControl = hoverControl;
1419 bool doesHover = q->testAttribute(attribute: Qt::WA_Hover);
1420 if (lastHoverControl != newHoverControl(pos) && doesHover) {
1421 q->update(lastHoverRect);
1422 q->update(hoverRect);
1423 return true;
1424 }
1425 return !doesHover;
1426}
1427
1428/*!
1429 \internal
1430 Returns the hover control at \a pos.
1431 This will update the hoverRect and hoverControl.
1432*/
1433QStyle::SubControl QAbstractSpinBoxPrivate::newHoverControl(const QPoint &pos)
1434{
1435 Q_Q(QAbstractSpinBox);
1436
1437 QStyleOptionSpinBox opt;
1438 q->initStyleOption(option: &opt);
1439 opt.subControls = QStyle::SC_All;
1440 hoverControl = q->style()->hitTestComplexControl(cc: QStyle::CC_SpinBox, opt: &opt, pt: pos, widget: q);
1441 hoverRect = q->style()->subControlRect(cc: QStyle::CC_SpinBox, opt: &opt, sc: hoverControl, widget: q);
1442 return hoverControl;
1443}
1444
1445/*!
1446 \internal
1447 Strips any prefix/suffix from \a text.
1448*/
1449
1450QString QAbstractSpinBoxPrivate::stripped(const QString &t, int *pos) const
1451{
1452 QStringView text(t);
1453 if (specialValueText.size() == 0 || text != specialValueText) {
1454 int from = 0;
1455 int size = text.size();
1456 bool changed = false;
1457 if (prefix.size() && text.startsWith(s: prefix)) {
1458 from += prefix.size();
1459 size -= from;
1460 changed = true;
1461 }
1462 if (suffix.size() && text.endsWith(s: suffix)) {
1463 size -= suffix.size();
1464 changed = true;
1465 }
1466 if (changed)
1467 text = text.mid(pos: from, n: size);
1468 }
1469
1470 const int s = text.size();
1471 text = text.trimmed();
1472 if (pos)
1473 (*pos) -= (s - text.size());
1474 return text.toString();
1475
1476}
1477
1478void QAbstractSpinBoxPrivate::updateEditFieldGeometry()
1479{
1480 Q_Q(QAbstractSpinBox);
1481 QStyleOptionSpinBox opt;
1482 q->initStyleOption(option: &opt);
1483 opt.subControls = QStyle::SC_SpinBoxEditField;
1484 edit->setGeometry(q->style()->subControlRect(cc: QStyle::CC_SpinBox, opt: &opt,
1485 sc: QStyle::SC_SpinBoxEditField, widget: q));
1486}
1487/*!
1488 \internal
1489 Returns \c true if a specialValueText has been set and the current value is minimum.
1490*/
1491
1492bool QAbstractSpinBoxPrivate::specialValue() const
1493{
1494 return (value == minimum && !specialValueText.isEmpty());
1495}
1496
1497/*!
1498 \internal Virtual function that emits signals when the value
1499 changes. Reimplemented in the different subclasses.
1500*/
1501
1502void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
1503{
1504}
1505
1506/*!
1507 \internal
1508
1509 Slot connected to the line edit's textChanged(const QString &)
1510 signal.
1511*/
1512
1513void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
1514{
1515 Q_Q(QAbstractSpinBox);
1516
1517 if (keyboardTracking) {
1518 QString tmp = t;
1519 int pos = edit->cursorPosition();
1520 QValidator::State state = q->validate(tmp, pos);
1521 if (state == QValidator::Acceptable) {
1522 const QVariant v = valueFromText(input: tmp);
1523 setValue(val: v, ep: EmitIfChanged, updateEdit: tmp != t);
1524 pendingEmit = false;
1525 } else {
1526 pendingEmit = true;
1527 }
1528 } else {
1529 pendingEmit = true;
1530 }
1531}
1532
1533/*!
1534 \internal
1535
1536 Virtual slot connected to the line edit's
1537 cursorPositionChanged(int, int) signal. Will move the cursor to a
1538 valid position if the new one is invalid. E.g. inside the prefix.
1539 Reimplemented in Q[Date|Time|DateTime]EditPrivate to account for
1540 the different sections etc.
1541*/
1542
1543void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
1544{
1545 if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
1546 ignoreCursorPositionChanged = true;
1547
1548 bool allowSelection = true;
1549 int pos = -1;
1550 if (newpos < prefix.size() && newpos != 0) {
1551 if (oldpos == 0) {
1552 allowSelection = false;
1553 pos = prefix.size();
1554 } else {
1555 pos = oldpos;
1556 }
1557 } else if (newpos > edit->text().size() - suffix.size()
1558 && newpos != edit->text().size()) {
1559 if (oldpos == edit->text().size()) {
1560 pos = edit->text().size() - suffix.size();
1561 allowSelection = false;
1562 } else {
1563 pos = edit->text().size();
1564 }
1565 }
1566 if (pos != -1) {
1567 const int selSize = edit->selectionStart() >= 0 && allowSelection
1568 ? (edit->selectedText().size()
1569 * (newpos < pos ? -1 : 1)) - newpos + pos
1570 : 0;
1571
1572 const QSignalBlocker blocker(edit);
1573 if (selSize != 0) {
1574 edit->setSelection(pos - selSize, selSize);
1575 } else {
1576 edit->setCursorPosition(pos);
1577 }
1578 }
1579 ignoreCursorPositionChanged = false;
1580 }
1581}
1582
1583/*!
1584 \internal
1585
1586 Initialises the QAbstractSpinBoxPrivate object.
1587*/
1588
1589void QAbstractSpinBoxPrivate::init()
1590{
1591 Q_Q(QAbstractSpinBox);
1592
1593 q->setLineEdit(new QLineEdit(q));
1594 edit->setObjectName("qt_spinbox_lineedit"_L1);
1595 validator = new QSpinBoxValidator(q, this);
1596 edit->setValidator(validator);
1597
1598 QStyleOptionSpinBox opt;
1599 // ### This is called from the ctor and thus we shouldn't call initStyleOption yet
1600 // ### as we only call the base class implementation of initStyleOption called.
1601 q->initStyleOption(option: &opt);
1602 spinClickTimerInterval = q->style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatRate, opt: &opt, widget: q);
1603 spinClickThresholdTimerInterval = q->style()->styleHint(stylehint: QStyle::SH_SpinBox_ClickAutoRepeatThreshold, opt: &opt, widget: q);
1604 q->setFocusPolicy(Qt::WheelFocus);
1605 q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::SpinBox));
1606 q->setAttribute(Qt::WA_InputMethodEnabled);
1607
1608 q->setAttribute(Qt::WA_MacShowFocusRect);
1609}
1610
1611/*!
1612 \internal
1613
1614 Resets the state of the spinbox. E.g. the state is set to
1615 (Keyboard|Up) if Key up is currently pressed.
1616*/
1617
1618void QAbstractSpinBoxPrivate::reset()
1619{
1620 Q_Q(QAbstractSpinBox);
1621
1622 buttonState = None;
1623 if (q) {
1624 if (spinClickTimerId != -1)
1625 q->killTimer(id: spinClickTimerId);
1626 if (spinClickThresholdTimerId != -1)
1627 q->killTimer(id: spinClickThresholdTimerId);
1628 spinClickTimerId = spinClickThresholdTimerId = -1;
1629 acceleration = 0;
1630 q->update();
1631 }
1632}
1633
1634/*!
1635 \internal
1636
1637 Updates the state of the spinbox.
1638*/
1639
1640void QAbstractSpinBoxPrivate::updateState(bool up, bool fromKeyboard /* = false */)
1641{
1642 Q_Q(QAbstractSpinBox);
1643 if ((up && (buttonState & Up)) || (!up && (buttonState & Down)))
1644 return;
1645 reset();
1646 if (q && (q->stepEnabled() & (up ? QAbstractSpinBox::StepUpEnabled
1647 : QAbstractSpinBox::StepDownEnabled))) {
1648 buttonState = (up ? Up : Down) | (fromKeyboard ? Keyboard : Mouse);
1649 int steps = up ? 1 : -1;
1650 if (keyboardModifiers & stepModifier)
1651 steps *= 10;
1652 q->stepBy(steps);
1653 spinClickThresholdTimerId = q->startTimer(interval: spinClickThresholdTimerInterval);
1654#if QT_CONFIG(accessibility)
1655 QAccessibleValueChangeEvent event(q, value);
1656 QAccessible::updateAccessibility(event: &event);
1657#endif
1658 }
1659}
1660
1661
1662/*!
1663 Initialize \a option with the values from this QSpinBox. This method
1664 is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
1665 to fill in all the information themselves.
1666
1667 \sa QStyleOption::initFrom()
1668*/
1669void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const
1670{
1671 if (!option)
1672 return;
1673
1674 Q_D(const QAbstractSpinBox);
1675 option->initFrom(w: this);
1676 option->activeSubControls = QStyle::SC_None;
1677 option->buttonSymbols = d->buttonSymbols;
1678 option->subControls = QStyle::SC_SpinBoxEditField;
1679 if (style()->styleHint(stylehint: QStyle::SH_SpinBox_ButtonsInsideFrame, opt: nullptr, widget: this))
1680 option->subControls |= QStyle::SC_SpinBoxFrame;
1681 if (d->buttonSymbols != QAbstractSpinBox::NoButtons) {
1682 option->subControls |= QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
1683 if (d->buttonState & Up) {
1684 option->activeSubControls = QStyle::SC_SpinBoxUp;
1685 } else if (d->buttonState & Down) {
1686 option->activeSubControls = QStyle::SC_SpinBoxDown;
1687 }
1688 }
1689
1690 if (d->buttonState) {
1691 option->state |= QStyle::State_Sunken;
1692 } else {
1693 option->activeSubControls = d->hoverControl;
1694 }
1695
1696 option->stepEnabled = style()->styleHint(stylehint: QStyle::SH_SpinControls_DisableOnBounds, opt: nullptr, widget: this)
1697 ? stepEnabled()
1698 : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled);
1699
1700 option->frame = d->frame;
1701}
1702
1703/*!
1704 \internal
1705
1706 Bounds \a val to be within minimum and maximum. Also tries to be
1707 clever about setting it at min and max depending on what it was
1708 and what direction it was changed etc.
1709*/
1710
1711QVariant QAbstractSpinBoxPrivate::bound(const QVariant &val, const QVariant &old, int steps) const
1712{
1713 QVariant v = val;
1714 if (!wrapping || steps == 0 || old.isNull()) {
1715 if (variantCompare(arg1: v, arg2: minimum) < 0) {
1716 v = wrapping ? maximum : minimum;
1717 }
1718 if (variantCompare(arg1: v, arg2: maximum) > 0) {
1719 v = wrapping ? minimum : maximum;
1720 }
1721 } else {
1722 const bool wasMin = old == minimum;
1723 const bool wasMax = old == maximum;
1724 const int oldcmp = variantCompare(arg1: v, arg2: old);
1725 const int maxcmp = variantCompare(arg1: v, arg2: maximum);
1726 const int mincmp = variantCompare(arg1: v, arg2: minimum);
1727 const bool wrapped = (oldcmp > 0 && steps < 0) || (oldcmp < 0 && steps > 0);
1728 if (maxcmp > 0) {
1729 v = ((wasMax && !wrapped && steps > 0) || (steps < 0 && !wasMin && wrapped))
1730 ? minimum : maximum;
1731 } else if (wrapped && (maxcmp > 0 || mincmp < 0)) {
1732 v = ((wasMax && steps > 0) || (!wasMin && steps < 0)) ? minimum : maximum;
1733 } else if (mincmp < 0) {
1734 v = (!wasMax && !wasMin ? minimum : maximum);
1735 }
1736 }
1737
1738 return v;
1739}
1740
1741/*!
1742 \internal
1743
1744 Sets the value of the spin box to \a val. Depending on the value
1745 of \a ep it will also emit signals.
1746*/
1747
1748void QAbstractSpinBoxPrivate::setValue(const QVariant &val, EmitPolicy ep,
1749 bool doUpdate)
1750{
1751 Q_Q(QAbstractSpinBox);
1752 const QVariant old = value;
1753 value = bound(val);
1754 pendingEmit = false;
1755 cleared = false;
1756 if (doUpdate) {
1757 updateEdit();
1758 }
1759 q->update();
1760
1761 if (ep == AlwaysEmit || (ep == EmitIfChanged && old != value)) {
1762 emitSignals(ep, old);
1763 }
1764}
1765
1766/*!
1767 \internal
1768
1769 Updates the line edit to reflect the current value of the spin box.
1770*/
1771
1772void QAbstractSpinBoxPrivate::updateEdit()
1773{
1774 Q_Q(QAbstractSpinBox);
1775 if (type == QMetaType::UnknownType)
1776 return;
1777 const QString newText = specialValue() ? specialValueText : prefix + textFromValue(n: value) + suffix;
1778 if (newText == edit->displayText() || cleared)
1779 return;
1780
1781 const bool empty = edit->text().isEmpty();
1782 int cursor = edit->cursorPosition();
1783 int selsize = edit->selectedText().size();
1784 const QSignalBlocker blocker(edit);
1785 edit->setText(newText);
1786
1787 if (!specialValue()) {
1788 cursor = qBound(min: prefix.size(), val: cursor, max: edit->displayText().size() - suffix.size());
1789
1790 if (selsize > 0) {
1791 edit->setSelection(cursor, selsize);
1792 } else {
1793 edit->setCursorPosition(empty ? prefix.size() : cursor);
1794 }
1795 }
1796 q->update();
1797}
1798
1799/*!
1800 \internal
1801
1802 Convenience function to set min/max values.
1803*/
1804
1805void QAbstractSpinBoxPrivate::setRange(const QVariant &min, const QVariant &max)
1806{
1807 Q_Q(QAbstractSpinBox);
1808
1809 clearCache();
1810 minimum = min;
1811 maximum = (variantCompare(arg1: min, arg2: max) < 0 ? max : min);
1812 cachedSizeHint = QSize();
1813 cachedMinimumSizeHint = QSize(); // minimumSizeHint cares about min/max
1814
1815 reset();
1816 if (!(bound(val: value) == value)) {
1817 setValue(val: bound(val: value), ep: EmitIfChanged);
1818 } else if (value == minimum && !specialValueText.isEmpty()) {
1819 updateEdit();
1820 }
1821
1822 q->updateGeometry();
1823}
1824
1825/*!
1826 \internal
1827
1828 Convenience function to get a variant of the right type.
1829*/
1830
1831QVariant QAbstractSpinBoxPrivate::getZeroVariant() const
1832{
1833 QVariant ret;
1834 switch (type) {
1835 case QMetaType::Int: ret = QVariant(0); break;
1836 case QMetaType::Double: ret = QVariant(0.0); break;
1837 default: break;
1838 }
1839 return ret;
1840}
1841
1842/*!
1843 \internal
1844
1845 Virtual method called that calls the public textFromValue()
1846 functions in the subclasses. Needed to change signature from
1847 QVariant to int/double/QDateTime etc. Used when needing to display
1848 a value textually.
1849
1850 This method is reimeplemented in the various subclasses.
1851*/
1852
1853QString QAbstractSpinBoxPrivate::textFromValue(const QVariant &) const
1854{
1855 return QString();
1856}
1857
1858/*!
1859 \internal
1860
1861 Virtual method called that calls the public valueFromText()
1862 functions in the subclasses. Needed to change signature from
1863 QVariant to int/double/QDateTime etc. Used when needing to
1864 interpret a string as another type.
1865
1866 This method is reimeplemented in the various subclasses.
1867*/
1868
1869QVariant QAbstractSpinBoxPrivate::valueFromText(const QString &) const
1870{
1871 return QVariant();
1872}
1873/*!
1874 \internal
1875
1876 Interprets text and emits signals. Called when the spinbox needs
1877 to interpret the text on the lineedit.
1878*/
1879
1880void QAbstractSpinBoxPrivate::interpret(EmitPolicy ep)
1881{
1882 Q_Q(QAbstractSpinBox);
1883 if (type == QMetaType::UnknownType || cleared)
1884 return;
1885
1886 QVariant v = getZeroVariant();
1887 bool doInterpret = true;
1888 QString tmp = edit->displayText();
1889 int pos = edit->cursorPosition();
1890 const int oldpos = pos;
1891
1892 if (q->validate(tmp, pos) != QValidator::Acceptable) {
1893 const QString copy = tmp;
1894 q->fixup(tmp);
1895 QASBDEBUG() << "QAbstractSpinBoxPrivate::interpret() text '"
1896 << edit->displayText()
1897 << "' >> '" << copy << '\''
1898 << "' >> '" << tmp << '\'';
1899
1900 doInterpret = tmp != copy && (q->validate(tmp, pos) == QValidator::Acceptable);
1901 if (!doInterpret) {
1902 v = (correctionMode == QAbstractSpinBox::CorrectToNearestValue
1903 ? variantBound(min: minimum, value: v, max: maximum) : value);
1904 }
1905 }
1906 if (doInterpret) {
1907 v = valueFromText(tmp);
1908 }
1909 clearCache();
1910 setValue(val: v, ep, doUpdate: true);
1911 if (oldpos != pos)
1912 edit->setCursorPosition(pos);
1913}
1914
1915void QAbstractSpinBoxPrivate::clearCache() const
1916{
1917 cachedText.clear();
1918 cachedValue.clear();
1919 cachedState = QValidator::Acceptable;
1920}
1921
1922QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
1923{
1924 Q_UNUSED(steps);
1925 return singleStep;
1926}
1927
1928// --- QSpinBoxValidator ---
1929
1930/*!
1931 \internal
1932 Constructs a QSpinBoxValidator object
1933*/
1934
1935QSpinBoxValidator::QSpinBoxValidator(QAbstractSpinBox *qp, QAbstractSpinBoxPrivate *dp)
1936 : QValidator(qp), qptr(qp), dptr(dp)
1937{
1938 setObjectName("qt_spinboxvalidator"_L1);
1939}
1940
1941/*!
1942 \internal
1943
1944 Checks for specialValueText, prefix, suffix and calls
1945 the virtual QAbstractSpinBox::validate function.
1946*/
1947
1948QValidator::State QSpinBoxValidator::validate(QString &input, int &pos) const
1949{
1950 if (dptr->specialValueText.size() > 0 && input == dptr->specialValueText)
1951 return QValidator::Acceptable;
1952
1953 if (!dptr->prefix.isEmpty() && !input.startsWith(s: dptr->prefix)) {
1954 input.prepend(s: dptr->prefix);
1955 pos += dptr->prefix.size();
1956 }
1957
1958 if (!dptr->suffix.isEmpty() && !input.endsWith(s: dptr->suffix))
1959 input.append(s: dptr->suffix);
1960
1961 return qptr->validate(input, pos);
1962}
1963/*!
1964 \internal
1965 Calls the virtual QAbstractSpinBox::fixup function.
1966*/
1967
1968void QSpinBoxValidator::fixup(QString &input) const
1969{
1970 qptr->fixup(input);
1971}
1972
1973// --- global ---
1974
1975/*!
1976 \internal
1977 Adds two variants together and returns the result.
1978*/
1979
1980QVariant operator+(const QVariant &arg1, const QVariant &arg2)
1981{
1982 QVariant ret;
1983 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
1984 qWarning(msg: "QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
1985 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
1986 switch (arg1.userType()) {
1987 case QMetaType::Int: {
1988 const int int1 = arg1.toInt();
1989 const int int2 = arg2.toInt();
1990 if (int1 > 0 && (int2 >= INT_MAX - int1)) {
1991 // The increment overflows
1992 ret = QVariant(INT_MAX);
1993 } else if (int1 < 0 && (int2 <= INT_MIN - int1)) {
1994 // The increment underflows
1995 ret = QVariant(INT_MIN);
1996 } else {
1997 ret = QVariant(int1 + int2);
1998 }
1999 break;
2000 }
2001 case QMetaType::Double: ret = QVariant(arg1.toDouble() + arg2.toDouble()); break;
2002#if QT_CONFIG(datetimeparser)
2003 case QMetaType::QDateTime: {
2004 QDateTime a2 = arg2.toDateTime();
2005 QDateTime a1 = arg1.toDateTime().addDays(QDATETIMEEDIT_DATE_MIN.daysTo(d: a2.date()));
2006 a1.setTime(a1.time().addMSecs(ms: a2.time().msecsSinceStartOfDay()));
2007 ret = QVariant(a1);
2008 break;
2009 }
2010#endif // datetimeparser
2011 default: break;
2012 }
2013 return ret;
2014}
2015
2016
2017/*!
2018 \internal
2019 Subtracts two variants and returns the result.
2020*/
2021
2022QVariant operator-(const QVariant &arg1, const QVariant &arg2)
2023{
2024 QVariant ret;
2025 if (Q_UNLIKELY(arg1.userType() != arg2.userType()))
2026 qWarning(msg: "QAbstractSpinBox: Internal error: Different types (%s vs %s) (%s:%d)",
2027 arg1.typeName(), arg2.typeName(), __FILE__, __LINE__);
2028 switch (arg1.userType()) {
2029 case QMetaType::Int: ret = QVariant(arg1.toInt() - arg2.toInt()); break;
2030 case QMetaType::Double: ret = QVariant(arg1.toDouble() - arg2.toDouble()); break;
2031 case QMetaType::QDateTime: {
2032 QDateTime a1 = arg1.toDateTime();
2033 QDateTime a2 = arg2.toDateTime();
2034 int days = a2.daysTo(a1);
2035 int secs = a2.secsTo(a1);
2036 int msecs = qMax(a: 0, b: a1.time().msec() - a2.time().msec());
2037 if (days < 0 || secs < 0 || msecs < 0) {
2038 ret = arg1;
2039 } else {
2040 QDateTime dt = a2.addDays(days).addSecs(secs);
2041 if (msecs > 0)
2042 dt.setTime(dt.time().addMSecs(ms: msecs));
2043 ret = QVariant(dt);
2044 }
2045 }
2046 default: break;
2047 }
2048 return ret;
2049}
2050
2051/*!
2052 \internal
2053 Multiplies \a arg1 by \a multiplier and returns the result.
2054*/
2055
2056QVariant operator*(const QVariant &arg1, double multiplier)
2057{
2058 QVariant ret;
2059
2060 switch (arg1.userType()) {
2061 case QMetaType::Int:
2062 ret = static_cast<int>(qBound<double>(INT_MIN, val: arg1.toInt() * multiplier, INT_MAX));
2063 break;
2064 case QMetaType::Double: ret = QVariant(arg1.toDouble() * multiplier); break;
2065#if QT_CONFIG(datetimeparser)
2066 case QMetaType::QDateTime: {
2067 double days = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg1.toDateTime().date()) * multiplier;
2068 const qint64 daysInt = qint64(days);
2069 days -= daysInt;
2070 qint64 msecs = qint64(arg1.toDateTime().time().msecsSinceStartOfDay() * multiplier
2071 + days * (24 * 3600 * 1000));
2072 ret = QDATETIMEEDIT_DATE_MIN.addDays(days: daysInt).startOfDay().addMSecs(msecs);
2073 break;
2074 }
2075#endif // datetimeparser
2076 default: ret = arg1; break;
2077 }
2078
2079 return ret;
2080}
2081
2082
2083
2084double operator/(const QVariant &arg1, const QVariant &arg2)
2085{
2086 double a1 = 0;
2087 double a2 = 0;
2088
2089 switch (arg1.userType()) {
2090 case QMetaType::Int:
2091 a1 = (double)arg1.toInt();
2092 a2 = (double)arg2.toInt();
2093 break;
2094 case QMetaType::Double:
2095 a1 = arg1.toDouble();
2096 a2 = arg2.toDouble();
2097 break;
2098#if QT_CONFIG(datetimeparser)
2099 case QMetaType::QDateTime:
2100 a1 = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg1.toDate());
2101 a2 = QDATETIMEEDIT_DATE_MIN.daysTo(d: arg2.toDate());
2102 a1 += arg1.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2103 a2 += arg2.toDateTime().time().msecsSinceStartOfDay() / (36e5 * 24);
2104 break;
2105#endif // datetimeparser
2106 default: break;
2107 }
2108
2109 return (a1 != 0 && a2 != 0) ? (a1 / a2) : 0.0;
2110}
2111
2112int QAbstractSpinBoxPrivate::variantCompare(const QVariant &arg1, const QVariant &arg2)
2113{
2114 switch (arg2.userType()) {
2115 case QMetaType::QDate:
2116 Q_ASSERT_X(arg1.userType() == QMetaType::QDate, "QAbstractSpinBoxPrivate::variantCompare",
2117 qPrintable(QString::fromLatin1("Internal error 1 (%1)").
2118 arg(QString::fromLatin1(arg1.typeName()))));
2119 if (arg1.toDate() == arg2.toDate()) {
2120 return 0;
2121 } else if (arg1.toDate() < arg2.toDate()) {
2122 return -1;
2123 } else {
2124 return 1;
2125 }
2126 case QMetaType::QTime:
2127 Q_ASSERT_X(arg1.userType() == QMetaType::QTime, "QAbstractSpinBoxPrivate::variantCompare",
2128 qPrintable(QString::fromLatin1("Internal error 2 (%1)").
2129 arg(QString::fromLatin1(arg1.typeName()))));
2130 if (arg1.toTime() == arg2.toTime()) {
2131 return 0;
2132 } else if (arg1.toTime() < arg2.toTime()) {
2133 return -1;
2134 } else {
2135 return 1;
2136 }
2137
2138
2139 case QMetaType::QDateTime:
2140 if (arg1.toDateTime() == arg2.toDateTime()) {
2141 return 0;
2142 } else if (arg1.toDateTime() < arg2.toDateTime()) {
2143 return -1;
2144 } else {
2145 return 1;
2146 }
2147 case QMetaType::Int:
2148 if (arg1.toInt() == arg2.toInt()) {
2149 return 0;
2150 } else if (arg1.toInt() < arg2.toInt()) {
2151 return -1;
2152 } else {
2153 return 1;
2154 }
2155 case QMetaType::Double:
2156 if (arg1.toDouble() == arg2.toDouble()) {
2157 return 0;
2158 } else if (arg1.toDouble() < arg2.toDouble()) {
2159 return -1;
2160 } else {
2161 return 1;
2162 }
2163 case QMetaType::UnknownType:
2164 if (arg2.userType() == QMetaType::UnknownType)
2165 return 0;
2166 Q_FALLTHROUGH();
2167 default:
2168 Q_ASSERT_X(0, "QAbstractSpinBoxPrivate::variantCompare",
2169 qPrintable(QString::fromLatin1("Internal error 3 (%1 %2)").
2170 arg(QString::fromLatin1(arg1.typeName()),
2171 QString::fromLatin1(arg2.typeName()))));
2172 }
2173 return -2;
2174}
2175
2176QVariant QAbstractSpinBoxPrivate::variantBound(const QVariant &min,
2177 const QVariant &value,
2178 const QVariant &max)
2179{
2180 Q_ASSERT(variantCompare(min, max) <= 0);
2181 if (variantCompare(arg1: min, arg2: value) < 0) {
2182 const int compMax = variantCompare(arg1: value, arg2: max);
2183 return (compMax < 0 ? value : max);
2184 } else {
2185 return min;
2186 }
2187}
2188
2189
2190QT_END_NAMESPACE
2191
2192#include "moc_qabstractspinbox.cpp"
2193

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