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

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